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

Zeppelin中Spark Scala语句报$iw类ClassCastException求助排查

解决Zeppelin中Spark Scala代码的ClassCastException($iw类加载器冲突)

问题现象

在Zeppelin中执行Scala编写的Spark语句时,抛出ClassCastException,提示来自不同类加载器的$iw类无法互相转换:

  • 一个来自org.apache.spark.repl.ExecutorClassLoader
  • 另一个来自scala.tools.nsc.interpreter.IMain$TranslatingClassLoader

异常栈信息:

org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 3.0 failed 1 times, most recent failure: Lost task 0.0 in stage 3.0 (TID 3) (192.168.1.153 executor driver): java.lang.ClassCastException: class $iw cannot be cast to class $iw ($iw is in unnamed module of loader org.apache.spark.repl.ExecutorClassLoader @69bd1241; $iw is in unnamed module of loader scala.tools.nsc.interpreter.IMain$TranslatingClassLoader @5a68e587)
    at org.apache.spark.sql.catalyst.expressions.GeneratedClass$GeneratedIteratorForCodegenStage1.deserializetoobject_doConsume_0$(Unknown Source)
    at org.apache.spark.sql.catalyst.expressions.GeneratedClass$GeneratedIteratorForCodegenStage1.processNext(Unknown Source)

但相同代码在spark-shell中可正常执行,示例代码如下:

import spark.implicits._
case class Flight(DEST_COUNTRY_NAME: String,
                  ORIGIN_COUNTRY_NAME: String,
                  count: BigInt)
val flightsDF = spark.read
  .parquet("/home/lebihan/dev/apprentissageDev/Spark-The-Definitive-Guide/data/flight-data/parquet/2010-summary.parquet/")
val flights = flightsDF.as[Flight]

// 执行转换操作
flights
  .filter(flight_row => flight_row.ORIGIN_COUNTRY_NAME != "Canada")
  .map(flight_row => flight_row)
  .take(5)

flights
  .take(5)
  .filter(flight_row => flight_row.ORIGIN_COUNTRY_NAME != "Canada")
  .map(fr => Flight(fr.DEST_COUNTRY_NAME, fr.ORIGIN_COUNTRY_NAME, fr.count + 5))

原因分析

$iw是Spark REPL为包装代码块生成的内部类,Zeppelin的解释器类加载机制与spark-shell存在差异:

  • spark-shell的REPL类加载器在Driver和Executor端保持一致
  • Zeppelin的解释器会使用独立的类加载器隔离不同笔记或代码块,导致Driver端(IMain$TranslatingClassLoader)和Executor端(ExecutorClassLoader)加载的$iw类属于不同的类实例,序列化/反序列化时触发类型转换异常。

排查与解决步骤

1. 调整Zeppelin Spark解释器的类加载优先级

在Zeppelin的Spark解释器设置中,添加或修改以下Spark配置:

  • spark.driver.userClassPathFirst=true:让Driver端优先加载用户类路径下的类
  • spark.executor.userClassPathFirst=true:让Executor端优先加载用户类路径下的类
  • zeppelin.spark.classloader.parents=first:调整Zeppelin解释器的类加载顺序,优先使用Spark的类加载器

2. 确保Scala版本完全一致

Spark 3.1.1默认适配Scala 2.12.x,检查以下内容:

  • 对比Zeppelin安装目录lib下的scala-library*.jar版本与Spark安装目录lib下的对应版本,必须完全一致
  • 在Zeppelin Spark解释器设置中开启zeppelin.spark.useSparkClassLoader=true,强制Zeppelin使用Spark自带的类加载器,避免依赖Zeppelin自身的Scala库
  • 若版本不一致,替换Zeppelin的Scala库为Spark对应版本的文件

3. 修改Zeppelin解释器隔离模式

Zeppelin默认使用共享解释器模式,容易引发类加载冲突:

  • 在Zeppelin的解释器管理页面,找到Spark解释器,将隔离模式从shared改为per note或per user,让每个笔记/用户使用独立的解释器实例

4. 优化代码编写方式

避免在Zeppelin代码块中直接定义自定义类(如case class),改为以下方式:

  • 将自定义类打包成独立的Jar文件,通过Zeppelin解释器的"依赖"功能添加到类路径中
  • 若必须在Zeppelin中定义类,将类定义放在单独的代码块中,执行完成后再运行后续的数据处理代码,避免跨代码块的类加载冲突

5. 切换Spark序列化方式为Kryo

Java序列化对类加载器敏感,切换为Kryo序列化可缓解类加载冲突:

  • 在Zeppelin Spark解释器设置中添加:
    spark.serializer=org.apache.spark.serializer.KryoSerializer
    spark.kryo.classesToRegister=com.yourpackage.Flight
    
    (将com.yourpackage.Flight替换为实际的类全限定名,若使用Jar包方式则需要对应路径)

内容的提问来源于stack exchange,提问作者Marc Le Bihan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 17:05:21