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!