如何在Spark 2.1执行器中加载更高版本的json4s?
我之前在Azure Spark 2.1环境里折腾json4s版本升级时,也碰到过类似的坑,给你梳理下问题根源和可行的解决办法:
首先,核心问题是版本冲突:Spark 2.1本身内置的是json4s 3.2.x系列版本,你手动升级到3.3.0后,Spark的类加载器会优先加载自带的旧版类,而你的代码用的是新版API,两者一打架,FieldSerializer自然就出问题了。另外,json4s 3.3.0对FieldSerializer的内部实现做了调整,和Spark 2.1的序列化机制适配起来确实容易出状况。
给你三个可行的解决方案:
1. 强制让Spark用你指定的json4s版本
在build.sbt里排除Spark自带的所有json4s依赖,确保整个应用统一用3.3.0版本:
libraryDependencies ++= Seq( "org.json4s" %% "json4s-native" % "3.3.0", "org.json4s" %% "json4s-ext" % "3.3.0" ).map(dep => dep.excludeAll( ExclusionRule(organization = "org.json4s") )) // 同时把Spark核心和SQL依赖里的json4s也排除掉 libraryDependencies += "org.apache.spark" %% "spark-core" % "2.1.0" % Provided excludeAll( ExclusionRule(organization = "org.json4s") ) libraryDependencies += "org.apache.spark" %% "spark-sql" % "2.1.0" % Provided excludeAll( ExclusionRule(organization = "org.json4s") )
这样就能彻底避免类版本冲突,让应用从头到尾用的都是你指定的3.3.0版本。
2. 调整FieldSerializer的写法
如果排除依赖后还是有问题,可能是3.3.0里renameTo的细节变了,你可以同时指定序列化和反序列化的字段映射:
import org.json4s._ import org.json4s.FieldSerializer._ implicit val formats: Formats = DefaultFormats + FieldSerializer[MyClass]( renameFrom("anotherName", "name"), // 反序列化时把anotherName映射为name renameTo("name", "anotherName") // 序列化时把name映射为anotherName )
另外别忘了确认你的MyClass继承了Serializable接口——Spark的分布式操作要求类必须是可序列化的,这一点很容易被忽略。
3. 回退到兼容的json4s版本
如果上面两种方案都搞不定,不如退一步:用json4s 3.2.13版本。这个版本已经修复了Double序列化的Bug,同时和Spark 2.1的兼容性拉满,不会出现FieldSerializer的奇怪问题。修改build.sbt:
libraryDependencies += "org.json4s" %% "json4s-native" % "3.2.13" libraryDependencies += "org.json4s" %% "json4s-ext" % "3.2.13"
3.2.13是3.2系列的最后一个稳定版,Bug修复到位,和Spark自带依赖的差异极小,冲突风险几乎为零。
最后提个小细节:在Azure上提交Spark应用时,最好用--packages参数指定json4s版本,确保集群节点和本地用的是同一个版本:
spark-submit --packages org.json4s:json4s-native_2.11:3.3.0,org.json4s:json4s-ext_2.11:3.3.0 --class your.main.Class your-app.jar
这样能避免本地跑正常、集群上报错的尴尬情况。
内容的提问来源于stack exchange,提问作者Don Branson

