You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EMR 6.6.0(Spark3.2.0)运行Spark-Scala任务报IncompatibleClassChangeError

EMR 6.6.0中Spark-Scala任务报IncompatibleClassChangeError的解决方法

问题场景

Spark-Scala任务在EMR 5.36.0(Spark 2.4.8、Scala 2.11)环境运行正常,但切换到EMR 6.6.0(Spark 3.2.0、Scala 2.12.15)时触发以下错误:

Exception in thread "main" java.lang.IncompatibleClassChangeError: Class org.json.JSONArray does not implement the requested interface java.lang.Iterable

对应的核心代码片段:

import scala.collection.JavaConverters._
val configJSON: JSONArray = fetchConfigs(configServerUri, configFiles)
configJSON.asScala.foreach(propertySource => {
  propertySource.asInstanceOf[JSONObject].getJSONObject("source").toMap.asScala.foreach(kv => {
    sparkAppConf.set(configReMap.getOrElse(kv._1, kv._1), kv._2.toString)
  })
})

pom中依赖的是org.json:json:20220924(该版本JSONArray已实现Iterable<Object>接口),且本地Spark 3.2.0环境运行正常。

问题原因

EMR 6.6.0集群的系统类路径中内置了旧版本的org.json库(如20140107等早期版本),这些版本的JSONArray并未实现Iterable接口。当任务提交到集群时,类加载器优先加载了系统自带的旧版本JSONArray类,而非你打包的新版本类,导致代码调用asScala时触发类不兼容错误。

本地环境正常是因为没有旧版本的org.json包干扰,类加载器直接使用了你依赖的新版本。

解决方法

方法1:用Maven Shade插件重命名org.json包(推荐)

通过重命名你依赖的org.json包,彻底避免和集群自带库的冲突。在pom.xml中添加Shade插件配置:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-shade-plugin</artifactId>
      <version>3.4.1</version>
      <executions>
        <execution>
          <phase>package</phase>
          <goals>
            <goal>shade</goal>
          </goals>
          <configuration>
            <relocations>
              <relocation>
                <pattern>org.json</pattern>
                <!-- 替换为你的自定义包名 -->
                <shadedPattern>com.yourteam.shaded.org.json</shadedPattern>
              </relocation>
            </relocations>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

重新打包后,插件会自动将jar中的org.json类重命名为你指定的包路径,集群类加载器会优先加载这个重命名后的类,不会再和系统自带库冲突。

方法2:强制指定依赖加载顺序

提交Spark任务时,显式指定org.json jar的路径,并配置驱动和Executor优先加载该jar:

spark-submit \
  --class com.yourcompany.YourMainClass \
  --jars /path/to/json-20220924.jar \
  --driver-class-path /path/to/json-20220924.jar \
  --conf spark.executor.extraClassPath=json-20220924.jar \
  your-application.jar

注意:这种方式依赖集群的类加载顺序配置,稳定性不如Shade插件,若集群有严格的类加载优先级限制可能失效。

方法3:替换为Spark内置的JSON处理工具

Spark 3.x内置了Jackson等JSON解析库,直接使用这些工具可以完全消除外部依赖冲突:

import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import scala.collection.JavaConverters._

val mapper = new ObjectMapper()
// 假设fetchConfigs返回的是JSON字符串,若不是则先转为字符串
val configJsonNode: JsonNode = mapper.readTree(fetchConfigs(configServerUri, configFiles).toString)

if (configJsonNode.isArray) {
  configJsonNode.elements().asScala.foreach(propertySourceNode => {
    val sourceNode = propertySourceNode.get("source")
    sourceNode.fields().asScala.foreach(kv => {
      val key = configReMap.getOrElse(kv.getKey, kv.getKey)
      sparkAppConf.set(key, kv.getValue.asText())
    })
  })
}

这种方式不需要额外引入org.json依赖,直接复用Spark自带的Jackson库,从根源上避免依赖冲突。

内容的提问来源于stack exchange,提问作者Ankit Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 16:16:09