Spark提交任务报Failed to load class主类加载失败问题咨询
这个报错本质是Spark提交端无法从指定Jar包中读取到主类的class文件,和配置内容本身的业务逻辑无关,结合你的场景,90%概率是以下原因导致:
你之前提交过同名的NextGenETL-testing.jar,当时的Jar包存在损坏(比如打包中断、增量编译不完整、上传时IO错误导致Jar包缺少主类class文件),被YARN分布式缓存缓存到了节点本地。YARN分布式缓存的默认资源校验逻辑是基于文件路径、文件大小、最后修改时间生成缓存key,当你仅修改properties文件又逐字符改回原内容时,新打出来的Jar包大小、修改时间刚好和旧的损坏包匹配,命中了坏的缓存,直接拉取本地损坏包运行,就会报类找不到。当你替换成随机字符串后,Jar包大小变化,缓存key失效,YARN重新上传完整的新Jar包,任务就正常运行。
另外你的环境存在一个潜在兼容坑:pom中配置用Java 11编译,但EMR上的Spark 2.4.4默认运行在Java 8环境,后续可能触发UnsupportedClassVersionError,建议把maven.compiler.source和maven.compiler.target改成1.8。日志中出现的SLF4J多绑定是警告信息,不影响主类加载,不需要优先处理。
出问题时按顺序执行以下命令即可快速定位:
- 先在本地验证Jar包本身是否完整:
# 检查主类是否存在于Jar包中 jar tf /home/ec2-user/localtesting/NextGenETL-testing.jar | grep SanitycheckForNewLine # 尝试本地加载主类,看是否报错 javap -cp /home/ec2-user/localtesting/NextGenETL-testing.jar com.monster.SanitycheckForNewLine
- 如果上述命令报错,说明是Maven构建环节出问题:大概率是增量编译bug导致class文件没被打进Jar,直接执行
mvn clean package全量重新打包即可,不要直接用mvn package做增量构建。如果javap提示class文件解析失败,说明class文件被破坏,检查pom里的maven-resources-plugin配置,不要开启对class、jar等二进制文件的过滤,过滤规则仅保留对properties、xml等文本资源文件生效。 - 如果上述命令正常输出类信息,说明Jar包本身没问题,就是YARN缓存了坏包:清理YARN分布式缓存,或者给Jar包加个时间戳后缀(比如
NextGenETL-testing-$(date +%s).jar)再提交,避免缓存命中即可。
不管是Scala代码还是配置文件触发的这类玄学问题,按分层排查法走,不要靠猜:
- 先排除构建产物问题:所有运行时的类、资源找不到问题,第一步永远是直接解压构建产物(Jar/War),检查里面的class文件、配置文件是否存在、内容是否正确,不要默认"代码写对了打包肯定没问题"。可以用
javap检查class文件能不能正常解析,用hexdump -C检查配置文件里有没有不可见的零宽字符、错误的换行符,不要仅靠肉眼判断。 - 再排除运行环境缓存问题:大数据组件(YARN/Spark/Flink)、Web容器、本地Maven仓库都有缓存机制,只要出现"改了无关内容就好、改回去又坏"的现象,第一时间清缓存,换个唯一文件名重新提交,90%的玄学问题都能解决。
- 最后排查依赖/版本兼容问题:如果前两步都没问题,就打印运行时的实际classpath,检查有没有依赖冲突、版本不匹配(比如Scala大版本不一致、JDK版本不兼容),这类问题往往会报看起来完全不相关的错误。
不要在业务逻辑里找原因,这类问题100%出在构建、缓存、兼容三个环节,和你写的业务代码/配置内容本身没有关系。
内容的提问来源于stack exchange,提问作者user9853453

