Hadoop classpath下的Jackson 和User jar内的Jackson冲突
此文章排查问题不单单指Flink,各种on YARN 集群环境都,本文都可以提供排查思路。
直接亮出报错
java.lang.NoSuchFieldError: FAIL_ON_SYMBOL_HASH_OVERFLO
at org.elasticsearch.common.xcontent.json.JsonXContent.<clinit>(JsonXContent.java:57)
情况是程序在IDE运行正常,打包上Yarn集群环境就报错。
排查思路
基本问题可以定位为类加载顺序出问题
- 在你会使用到Jackson
ObjectMapper.class.getProtectionDomain().getCodeSource().getLocation()
类的地方打印出class加载的文件目录,查看类加载的路径是否为你想要的路径(User jar path) - 当你定位到Jackson类加载路径不对像我这样
file:/usr/**/hadoop/lib/jackson-databind-2.2.3.jar
从hadoop classpath下加载了ObjectMapper
而不是User jar - 查看程序启动时候的classpath 设置。执行yarn logs -applicationId *** 在日志中查看程序启动时候的classpath,查看User jar 是否在classpath列表中。以flink 1.9.1为例,user jar 是不会被加入到classpath中的。
- 解决思路,想办法把你的User jar 加入到classpath中,并且需要排在最前面,以flink为例解决方法有2种
- 将你的User jar 加入到 flink lib下
- 修改bin/flink脚本,将你的user jar (USER_JAR_CLASSPATH)加入到脚本最后一行, -classpath 后面
exec $JAVA_RUN $JVM_ARGS "${log_setting[@]}" -classpath "`manglePathList "$USER_JAR_CLASSPATH:$CC_CLASSPATH:$INTERNAL_HADOOP_CLASSPATHS"`" org.apache.flink.client.cli.CliFrontend "$@"
写在后面我的排查过程
因为之前有看过flink的反向类加载问题,于是从flink run 脚本开始看,一直跟代码
CliFrontend#bulidProgram line 799
PackagedProgram#PackagedProgram line 221
JobWithJars#BuildUserCodeClassLoad line 142
return FlinkUserCodeClassLoaders.parentFirst(urls, parent);
可以看到代码里面写死了使用了parentFirst,根据官方文档里面描述的反向类加载,代码的意思是首先从classpath下加载Class,而不是从user。这跟我们的意愿不一样。
FLink 1.9.2 和 1.10 已经修复了这个问题 https://issues.apache.org/jira/browse/FLINK-13749