来自Spark职位的Akka Stream写入kafka

时间:2017-08-29 14:13:52

标签: scala apache-kafka spark-streaming akka-stream

愿意最有效地将数据写回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,但我不确定这是最有效的方法。

1 个答案:

答案 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是线程安全的,所以这也不会导致这方面的问题。