Apache spark 检查点设置时foreachRDD()中使用的对象的序列化

Apache spark 检查点设置时foreachRDD()中使用的对象的序列化,apache-spark,spark-streaming,rdd,avro,kryo,Apache Spark,Spark Streaming,Rdd,Avro,Kryo,根据我所阅读的文件和资料,Spark Streaming的foreachRDD(someFunction)将只在驱动程序进程中执行someFunction本身,尽管如果在RDD上执行操作,那么它们将在RDD所在的执行器上执行 所有这些对我来说都很有用,尽管我注意到,如果我打开检查点,那么spark似乎正在尝试序列化foreachRDD(someFunction)中的所有内容并发送到某个地方——这对我来说是一个问题,因为使用的一个对象是不可序列化的(即schemaRegistryClient)。我

根据我所阅读的文件和资料,Spark Streaming的foreachRDD(someFunction)将只在驱动程序进程中执行someFunction本身,尽管如果在RDD上执行操作,那么它们将在RDD所在的执行器上执行

所有这些对我来说都很有用,尽管我注意到,如果我打开检查点,那么spark似乎正在尝试序列化foreachRDD(someFunction)中的所有内容并发送到某个地方——这对我来说是一个问题,因为使用的一个对象是不可序列化的(即schemaRegistryClient)。我试过Kryo连载器,但也没有成功

如果我关闭检查点,序列化问题就会消失

有没有办法让Spark在继续使用检查点的同时不序列化foreachRDD(someFunc)中使用的内容


非常感谢。

这里有几件事很重要:

  • 您不能在对worker(在RDD内部)执行的代码中使用此客户机
  • 您可以使用临时客户端字段创建对象,并在作业重新启动后重新创建该对象。可以找到如何实现这一点的示例
  • 同样的原理也适用于广播和累加器变量
  • 检查点保存数据、作业元数据和代码逻辑。当代码更改时,检查点将无效
  • 有没有办法让Spark在继续使用检查点的同时不序列化foreachRDD(someFunc)中使用的内容

    检查点不应该和你的问题有任何关系。根本的问题是,您有一个不可序列化的对象实例,需要将其发送给工作人员

    当您有这样的依赖关系时,Spark中有一个通用模式可供使用。您可以创建一个具有惰性瞬态属性的
    对象,该属性将在需要时加载到工作节点中:

    object RegisteryWrapper {
      @transient lazy val schemaClient: SchemaRegisteryClient = new SchemaRegisteryClient()
    }
    
    当您需要在foreachRDD中使用它时:

    someStream.foreachRDD { 
       rdd => rdd.foreachPartition { iterator => 
           val schemaClient = RegisteryWrapper.schemaClient
           iterator.foreach(schemaClient.send(_))
      }
    }
    

    问题可能在于检查点数据,如果您更改了代码中的任何内容,则需要删除旧的检查点元数据。

    非常感谢Yuval,您的建议很有效。接下来的一个问题是,关于这句话“有一个惰性瞬态属性,它将在需要时加载到工作节点内部”——根据这个[link](),它表示“Scala为根本不应该序列化的字段提供了@transient注释”——因此,如果工作节点上需要schemaRegistryClient,如果没有序列化,它怎么可能被发送给工人(或者除了它的出生地司机以外的任何地方)?@howard它没有发送给工人。但是,别忘了工人有操作所需的所有罐子。这意味着,如果需要一个实例,工作者将加载相关类并创建一个实例。根本不需要序列化。我明白了,因为schemaRegistry被定义为一个对象变量,所以它在对象定义中,因此工作人员可以仅从jar本身构造它。类似的规则也适用于检查点,它不是序列化的,也不保存在HDFS上,但驱动程序也有jar,可以从对象定义构建它-因此将其作为对象定义、@transient注释和惰性前缀都是此模式工作所必需的。再次感谢@Yuval@YuvalItzchakov OP询问如何序列化对象。是否可以在
    foreachRDD
    中创建一个
    不可序列化的
    对象,或者Spark在所有
    foreach*
    函数中强制执行序列化规则?@CᴴᴀZ如果在
    foreachRDD
    函数中分配对象,则Spark必须确保其可序列化,因为它必须将其发送到RDD的每个分区(当然,这假设您正在调用
    RDD.foreach
    RDD.map
    )。另一方面,在
    rdd.foreachPartition
    中,如果在那里分配,则分配会在每个分区本地进行,因此不需要序列化。