问题现象:
应用基于Spark2.3编译,在编译时添加了依赖json4s的包,指定版本为3.2.10,在提交spark作业后,抛出如下
java.lang.NoSuchMethodError: org.json4s.jackson.JsonMethods$.render(Lorg/json4s/JsonAST$JValue;)Lorg/json4s/JsonAST$JValue;
问题分析
json4s项目出现小版本间api不兼容
查看相关lib下的jar包,可以确认json4s-jackson_2.11-3.2.10.jar的jar包,不应当找不到该jar包。
于是怀疑jar包冲突,查看Spark服务相关目录的jar包发现存在json4s-jackson_2.11-3.2.11.jar,因此怀疑该jar包版本不一致,发生冲突。
正常来讲,小版本的变更不应当发生接口不兼容的情况,然而,google一下就发现确实是json4s项目的版本不兼容问题。
json4s项目的源码与jar包不对应
于是查看一下json4s在3.2.10以及3.2.11版本的的JsonMethods类:
// 3.2.10 def render(value: JValue): JValue = value // 3.2.11 def render(value: JValue): JValue = value // 3.2.11-2.10 或3.2.11-2.12 def render(value: JValue)(implicit formats: Formats = DefaultFormats): JValue = formats.emptyValueStrategy.replaceEmpty(value)
由于应用编译时依赖的是scala2.11版本,如果仅仅从代码上分析,并不会出现不兼容的情况。
于是进一步查看spark服务中依赖的json4s的jar包,反编译之后分析可以看出,其使用的源码应当是3.2.11-2.10或3.2.11-2.12版本,这又是json4s的坑了
查看两个版本反编译之后的代码,可以很明确的看出在3.2.11版本中确实不存在 org.json4s.jackson.JsonMethods$.render(Lorg/json4s/JsonAST$JValue;)Lorg/json4s/JsonAST$JValue
方法
问题复现
即使使用以上不同代码,如果应用程序代码中依旧可以调用不同版本的json4s,因为对于带有隐式参数的render函数来说,由于其包含了default值,我们依旧可以通过调用render(JValue)来调用。于是测了一把带有隐式默认值的函数的调用。
测试函数如下 :
-
带有隐式默认参数的函数(code1)
package com.cyq.test trait TClass { def fun(a : Any)(implicit b : String = "abcd") : Unit = { println("Enter in class impl") } } object TClass extends TClass
-
不带有隐式默认参数的函数(code2)
package com.cyq.test trait TClass { def fun(a : Any): Unit = { println("Enter in class without impl") } } object TClass extends TClass
-
测试函数(code3)
object MyTest { def main(args: Array[String]): Unit = { println("Starting") TClass.fun(100) println("End") } }
-
测试步骤
-
基于code1,code2 分别打包为TestWithImpl.jar,TestWithoutImpl.jar
-
基于TestWithoutImpl.jar 将code3打包为NoMethodTest.jar
-
分别运行如下命令,问题得以复现:
image
-
-
基于TestWithImpl.jar 将code3打包为NoMethodTest.jar
-
分别运行如下命令,问题得以解决:
image
解决方案
基于应用代码,不做修改,选择3.2.11版本的json4s版本,重新编译即可。
结论
基于不同依赖版本的jar包编译的编译出来的应用程序(jar包)
,可能由于依赖版本的不兼容,导致运行异常。
网友评论