愿意最有效地将数据写回kafka,我有兴趣使用Akka Stream将我的RDD分区写回Kafka。
问题是我需要一种方法来为每个执行器而不是每个分区创建一个actor系统,这将是荒谬的。最终可能在一个JVM上的一个节点上有8个actorSystems。但是每个分区有一个Stream很好。
有人已经这样做了吗?
我的理解是,演员系统不能被序列化,因此无法进行 发送的广播变量是每个遗嘱执行人。
如果有经验可以找到解决方案并进行测试,请分享一下吗?
否则我总能回到https://index.scala-lang.org/benfradet/spark-kafka-writer/spark-kafka-0-10-writer/0.3.0?target=_2.11,但我不确定这是最有效的方法。
答案 0 :(得分:2)
您始终可以使用actor系统定义全局延迟val:
object Execution {
implicit lazy val actorSystem: ActorSystem = ActorSystem()
implicit lazy val materializer: Materializer = ActorMaterializer()
}
然后,您只需在要使用Akka Streams的任何类中导入它:
import Execution._
val stream: DStream[...] = ...
stream.foreachRDD { rdd =>
...
rdd.foreachPartition { records =>
val (queue, done) = Source.queue(...)
.via(Producer.flow(...))
.toMat(Sink.ignore)(Keep.both)
.run() // implicitly pulls `Execution.materializer` from scope,
// which in turn will initialize `Execution.actorSystem`
... // push records to the queue
// wait until the stream is completed
Await.result(done, 10.minutes)
}
}
以上是一种伪代码,但我认为应该传达一般的想法。
这样,只要需要,系统就会在每个执行程序JVM上初始化一次。此外,您可以使actor系统“守护进程”,以便在JVM完成时自动关闭:
object Execution {
private lazy val config = ConfigFactory.parseString("akka.daemonic = on")
.withFallback(ConfigFactory.load())
implicit lazy val actorSystem: ActorSystem = ActorSystem("system", config)
implicit lazy val materializer: Materializer = ActorMaterializer()
}
我们在Spark工作中这样做,它完美无瑕。
这可以在没有任何广播变量的情况下工作,当然,它可以用于各种Spark作业,流媒体或其他方式。因为系统是在单个对象中定义的,所以保证每个JVM实例只初始化一次(模数为各种类加载器的shenanigans,但它在Spark的上下文中并不重要),因此即使放置了一些分区在相同的JVM上(可能在不同的线程中),它只会初始化actor系统一次。 lazy val
确保初始化的线程安全,ActorSystem
是线程安全的,所以这也不会导致这方面的问题。