Scala 将Akka源转换为火花输入流的惯用方法

Scala 将Akka源转换为火花输入流的惯用方法,scala,apache-spark,akka,spark-streaming,akka-stream,Scala,Apache Spark,Akka,Spark Streaming,Akka Stream,我基本上是在试图做与被要求的相反的事情;也就是说,使用源[a]将元素推入输入流[a] 到目前为止,我已经成功地组合了一个实现,它使用了一个类似于ActorWordCount的Feeder actor和Receiver actor,但这似乎有点复杂,所以我想知道是否有更简单的方法。Ref: 你可以这样做: class StreamStopped extends RuntimeException("Stream stopped") // Serializable factory class cas

我基本上是在试图做与被要求的相反的事情;也就是说,使用
源[a]
将元素推入
输入流[a]

到目前为止,我已经成功地组合了一个实现,它使用了一个类似于
ActorWordCount
的Feeder actor和Receiver actor,但这似乎有点复杂,所以我想知道是否有更简单的方法。

Ref:

你可以这样做:

class StreamStopped extends RuntimeException("Stream stopped")

// Serializable factory class
case class SourceFactory(start: Int, end: Int) {
  def source = Source(start to end).map(_.toString)
}

class CustomReceiver(sourceFactory: SourceFactory)
  extends Receiver[String](StorageLevel.MEMORY_AND_DISK_2) with Logging {

  implicit val materializer = ....

  def onStart() {
    sourceFactory.source.runForEach { e =>
      if (isStopped) {
        // Stop the source
        throw new StreamStopped
      } else {
        store(e)
      }
    } onFailure {
      case _: StreamStopped => // ignore
      case ex: Throwable => reportError("Source exception", ex)
    }
  }

  def onStop() {}
}

val customReceiverStream = ssc.receiverStream(new CustomReceiver(SourceFactory(1,100))

编辑:5天后自我接受,因为没有好的答案

我已经将基于Actor的实现提取到一个lib中,到目前为止,它一直在为我工作。当这个问题的更好的解决方案出现时,我将更新或弃用lib

其用途如下:

// InputDStream can then be used to build elements of the graph that require integration with Spark
val (inputDStream, feedDInput) = Streaming.connection[Int]()
val source = Source.fromGraph(GraphDSL.create() { implicit builder =>

  import GraphDSL.Implicits._

  val source = Source(1 to 10)

  val bCast = builder.add(Broadcast[Int](2))
  val merge = builder.add(Merge[Int](2))

  val add1 = Flow[Int].map(_ + 1)
  val times3 = Flow[Int].map(_ * 3)
  source ~> bCast ~> add1 ~> merge
            bCast ~> times3 ~> feedDInput ~> merge

  SourceShape(merge.out)
})

val reducedFlow = source.runWith(Sink.fold(0)(_ + _))
whenReady(reducedFlow)(_ shouldBe 230)

val sharedVar = ssc.sparkContext.accumulator(0)
inputDStream.foreachRDD { rdd =>
  rdd.foreach { i =>
    sharedVar += i
  }
}
ssc.start()
eventually(sharedVar.value shouldBe 165)

在使用前面提到的Actor变量之前,我尝试过类似的方法,但在运行时,Spark尝试序列化
源代码
,结果失败。很抱歉,我不是Spark专家,没有注意到
接收器
必须是可序列化的。在这种情况下,一种选择是,不传递源实例,而是传递创建源的信息(以工厂对象的形式),然后在
Receiver\onStart
中创建源。请参阅我更新的示例。很公平,如果可以从接收器内部启动流,那么这可能是一种方法,但我特别希望使用现有的
源[a]
,而不是从接收器内部构建源。如果我们可以使用现有的
源代码
,我们可以很容易地将其推广到任何现有的Akka流(或流的转换),这很好。在Akka流中,
Source[a]
Graph
通常只是执行流的蓝图。如果您打算共享“蓝图”,那么您是将源[A]作为参数传递,还是让它在接收器内创建,在运行时没有什么区别,因为在这两种情况下,具体化都发生在接收器内。但是,如果您希望将
物化的
数据流馈送到数据流中,那么您最初的馈送器/接收器参与者方法实际上是有意义的。如果我有一个
源[a]
,需要拆分或广播,或者在其他流中重用,而将内容推送到源中的物理资源一次仅限于一个连接(例如,网络摄像头,取决于驱动程序),我认为从接收器中创建和运行流与传入现有的
/
/
之间的区别很大。此外,从效率角度来看,即使没有这种限制,能够重用现有的已定义的源代码也更好。有什么进展吗?您的库已经有一段时间没有更新了。。谢谢不,没什么不寻常的:)我写这篇文章是为了我当时正在做的几个小项目,但现在不需要再去碰了。如果您感兴趣,请随时发送PR!