EMR集群Spark-submit JAR时遭遇ClassNotFoundException问题排查
看起来你遇到的问题很典型——本地Zeppelin里跑正常,但打包成jar提交到EMR就找不到主类。我来帮你一步步排查和解决:
1. 先确认本地打包的Jar里确实包含目标类
这是最常见的原因:maven打包时没把你的主类包含进去。你可以在本地执行这个命令检查:
jar tf SparkApplication-SQL-jar-with-dependencies.jar | grep financialLineItem/FinancialLineItem.class
如果没有任何输出,说明你的jar里根本没有这个类。这时候要检查:
- 项目结构是否正确:
financialLineItem包是否在src/main/java(或scala)目录下,类FinancialLineItem是否在这个包里 - 执行
mvn clean package时有没有报错?如果有编译错误,maven不会生成完整的jar - 检查maven-assembly-plugin的配置:你的pom里已经指定了
jar-with-dependencies,但要确保插件在package阶段正常执行。可以看target目录下是否生成了两个jar:一个普通的jar,一个带-jar-with-dependencies后缀的jar,后者才是包含所有依赖和你的类的包。
2. 检查S3上的Jar是否完整
有时候上传到S3的过程中文件会损坏,或者你传错了jar文件。建议把S3上的jar下载回本地,再用上面的jar tf命令检查是否包含目标类。如果确实没有,重新打包后再上传一次。
3. 调整Maven依赖的Scope,避免和EMR集群的Spark冲突
你本地用的Spark版本是2.2.0,但EMR集群自带的Spark版本可能和这个不一致,而且集群已经自带了Spark和Scala的核心类库。如果你的jar里包含了这些依赖,会导致类加载冲突,甚至找不到主类。
修改pom.xml里的Spark和Scala依赖,把它们的scope设为provided:
<dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> <version>2.11.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_2.11</artifactId> <version>2.2.0</version> <scope>provided</scope> </dependency>
这样maven打包时就不会把这些依赖打进jar里,而是用EMR集群自带的版本,避免冲突。
4. 确认主类的全路径完全正确
Java的包名和类名是大小写敏感的!比如如果你的包名是FinancialLineItem(首字母大写),而你在命令里写的是financialLineItem(小写),就会找不到类。仔细核对:
- 代码里的包声明:
package financialLineItem; - 类名:
public class FinancialLineItem { ... } - spark-submit命令里的
--class参数:financialLineItem.FinancialLineItem
三者必须完全一致,包括大小写。
5. 用Client模式调试,查看更详细的日志
在EMR主节点上用client模式运行spark-submit,日志会直接输出在控制台,更容易找到问题:
spark-submit --deploy-mode client --class financialLineItem.FinancialLineItem s3://path/SparkApplication-SQL-jar-with-dependencies.jar
如果还是报错,控制台会给出更详细的堆栈信息,帮你定位是类找不到,还是依赖冲突导致的加载失败。
另外,Zeppelin里运行正常是因为它直接在集群的客户端节点加载你的代码,用的是集群自带的Spark环境,没有经过打包环节,所以不会遇到类路径或依赖冲突的问题。
内容的提问来源于stack exchange,提问作者Sudarshan kumar

