背景:
项目中需要将常用的几个spark jar包打包成一个大的jar包,然后将此jar包上传到hdfs,在提交到yarn时使用参数spark.yarn.jars指向hdfs上的目录,客户端程序(即spark业务逻辑的程序)在pom中可以将spark相关的包都设置为provided,这样就不需要再每次提交任务时都向hdfs上传一次spark相关的jar包。
问题:
java.lang.ClassNotFoundException: Failed to find data source: text. Please find packages at http://spark.apache.org/third-party-projects.html

程序中会使用到数据源org.apache.spark.sql.execution.datasources.text.TextFileFormat,该类在spark-sql jar包中,TextFileFormat类被spark框架加载的方式为使用ServiceLoad方式,即在META-INF/services/org.apache.spark.sql.sources.DataSourceRegister中配置需要ServiceLoad加载的类全路径。
TextFileFormat的加载方式的原理是之前就已知的,但奇怪的是一直报找不到该类,查找了一下午试过各种方式都没解决,最后突然想到检查下打完包后的spark-assembly包里的META-INF/services,此时发现META-INF/services/org.apache.spark.sql.sources.DataSourceRegister中的内容只有两条,和spark-sql jar中的内容不一致,联想到刚才在spark-hive jar包中看到后这两条记录,基本确定是同名文件被覆盖了,先手动拷贝spark-sql jar的META-INF/services/org.apache.spark.sql.sources.DataSourceRegister中的内容到spark-assembly的对应文件中,重新提交任务后能够正常运行。
解决方案:
每次手动拷贝不太现实,时间一长也容易忘记,因此需要自动化的方式。经查找资料将maven-assembly-plugin插件改为maven-shade插件,在该插件中配置org.apache.maven.plugins.shade.resource.AppendingTransformer实现同名文件内容合并。
最终的插件配置如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.apache.spark.deploy.yarn.ApplicationMaster</mainClass>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/services/org.apache.spark.sql.sources.DataSourceRegister</resource>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>