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

Spark Structured Streaming提交含JmsWriter的Jar仍报ClassNotFoundException如何解决

问题根因

你遇到的ClassNotFoundException是Executor侧反序列化JmsWriter对象时,无法在自身类路径下找到对应类导致的,同时你的JmsWriter实现还存在隐藏的序列化问题,具体原因和解决方法如下:

解决方法

1. 确保自定义Jar分发到所有Executor

Spark运行时Driver和Executor使用独立的类加载器,仅将Jar作为spark-submit的主Jar参数仅会将其加载到Driver的类路径,Executor无法访问:

  • 提交任务时添加--jars参数指定你的Jar路径,Spark会自动将该Jar分发到所有Executor的类路径下:
    spark-submit \
    --class 你的主类全限定名 \
    --jars output-jms.jar \
    # 其他你的任务参数
    output-jms.jar
    
  • 如果运行在YARN/K8s集群环境,建议将Jar上传到所有节点都可访问的共享存储(如HDFS),--jars参数填写HDFS路径即可。
  • 也可以将项目打成包含所有业务依赖(ActiveMQ相关依赖等)的Fat Jar,注意Spark、Scala相关依赖要设置为provided避免和集群内置依赖冲突,直接提交Fat Jar即可无需额外加--jars。

2. 修复JmsWriter的序列化问题

即使解决了类找不到的问题,你当前的JmsWriter代码也会报序列化错误:
jmsConnectionFactory字段未加@transient注解,而ActiveMQ的连接工厂是不可序列化的,Spark序列化JmsWriter对象分发到Executor时会失败,需要修改代码:

class JmsWriter(username: String, password: String, brokerUrl: String, clientId: String, topic: String) extends ForeachWriter[Row] with Logging {

  // 加@transient注解避免序列化,改为lazy在open方法调用时才初始化
  @transient lazy val jmsConnectionFactory = JmsConnector.initActiveMqConnectionFactory(username, password, brokerUrl, clientId)
  @transient var connection: Connection = _
  @transient var jmsSession: Session = _
  @transient var jmsProducer: MessageProducer = _

  override def open(partitionId: Long, epochId: Long): Boolean = {
    connection = {
      val cn = jmsConnectionFactory.createConnection()
      cn.setClientID(clientId)
      cn.start()
      logInfo("ActiveMQ connection created")
      cn
    }
    jmsSession = JmsConnector.initActiveMqSession(connection)
    jmsProducer = JmsConnector.initActiveMqProducer(jmsSession, topic)
    true
  }

  override def process(value: Row): Unit = jmsProducer.send(jmsSession.createTextMessage(value.mkString("")))

  override def close(errorOrNull: Throwable): Unit = {
    if (errorOrNull != null) logError("Error during writing the stream: " + errorOrNull)
    jmsProducer.close()
    jmsSession.close()
    connection.close()
  }
}

3. 验证类是否正常打包

先解压你的output-jms.jar检查是否存在对应类文件:

jar tf output-jms.jar | grep com.addmeaning.output.jms.spark.service.JmsWriter

如果没有输出,说明打包时没有将该类打入Jar,需要修改你的构建配置(maven/gradle/sbt)确保编译后的类正常打入Jar包。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.12 17:54:03