我有两个流程:
val a: Flow[Input, Data, NotUsed] =...
val b: Flow[Input, Unit, NotUsed] =...
第一个流程是我关心的数据事件流,第二个流程是“信号”流,也就是说,我真的只想在{{1}中收到元素时向下游发送Data
}}。
我曾想过使用b
之类的东西,但这似乎只在流和源之间起作用(尽管Akka文档暗示它也支持压缩流)。
我想念什么?
谢谢
答案 0 :(得分:1)
def zip[U](that: Graph[SourceShape[U], _]): Repr[(Out, U)]
def zipWith[Out2, Out3](that: Graph[SourceShape[Out2], _])(combine: (Out, Out2) => Out3): Repr[Out3]
两种方法都期望Source
。
用另一个Flow
压缩Flow
并不像人们想象的那么琐碎(例如,第二个Flow
可能每个输入元素使用mapConcat
生成多个元素)
您可以考虑构建自定义GraphStage
,如以下简单的示例所示:
case class DataIn(id: Int)
case class DataOut(content: String)
case class Signal(s: Int)
class ZipperFlow extends GraphStage[FlowShape[(DataIn, Signal), DataOut]] {
val in = Inlet[(DataIn, Signal)]("ZipperFlow.in")
val out = Outlet[DataOut]("ZipperFlow.out")
override val shape = FlowShape.of(in, out)
override def createLogic(attr: Attributes): GraphStageLogic =
new GraphStageLogic(shape) {
setHandler(in, new InHandler {
override def onPush(): Unit = {
push(out, DataOut("content-" + grab(in)._1.id))
}
})
setHandler(out, new OutHandler {
override def onPull(): Unit = {
pull(in)
}
})
}
}
测试ZipperFlow
:
implicit val system = ActorSystem("system")
implicit val materializer = ActorMaterializer()
implicit val ec = system.dispatcher
val dataSource = Source(1 to 5).map(DataIn(_))
val signalSource = Source(1 to 5).map(Signal(_))
val sink: Sink[DataOut, Future[Done]] = Sink.foreach(println)
dataSource.zip(signalSource).via(new ZipperFlow).runWith(sink)
// DataOut(content-1)
// DataOut(content-2)
// DataOut(content-3)
// DataOut(content-4)
// DataOut(content-5)
答案 1 :(得分:0)
这可以使用merge
中的akka-streams graphs来实现
更新:
正确的是zip
示例:
import akka.NotUsed
import akka.actor.ActorSystem
import akka.stream.scaladsl._
import akka.stream.{ActorMaterializer, ClosedShape}
object Application extends App {
implicit val sys: ActorSystem = ActorSystem()
implicit val mat: ActorMaterializer = ActorMaterializer()
val flowX: Flow[Int, String, NotUsed] = Flow[Int].map(i => (i + 10).toString)
val flowY: Flow[Int, Long, NotUsed] = Flow[Int].map(i => (i * 2).toLong)
RunnableGraph.fromGraph(GraphDSL.create(flowX, flowY)((_, _)) { implicit builder =>
(flowX, flowY) =>
import GraphDSL.Implicits._
val broadcast = builder.add(Broadcast[Int](2))
val zip = builder.add(Zip[String, Long])
Source((1 to 10).toList) ~> broadcast.in
broadcast ~> flowX ~> zip.in0
broadcast ~> flowY ~> zip.in1
zip.out ~> Sink.foreach(println)
ClosedShape
}).run()
}
flowX
和flowY
是用于图形创建的参数。在constructing graph
部分中,您可以找到用于拆分和合并流的不同情况(扇出+扇入)。使用图要比使用线性流更难。也许仅创建具有流动形状(1个输入,1个输出)的partial graph是有意义的-因此用户将其视为常规流动(但隐藏了复杂性)。我个人建议不要使用一般的图形,因为它很难测试(很难找到错误或性能下降),尽管在某些情况下这是一个很棒的功能
您可以找到许多methods来使用不同数量的参数来创建图形。此外,您可以为图形创建提供不同的输入参数-不同的源,流,汇。
答案 2 :(得分:0)
implicit class FlowExt[In, Out, Mat](flow: Flow[In, Out, Mat]) {
/**
*
* @param f
* @tparam Out2
* @return
*/
def zipFlow[Out2](f: Flow[In, Out2, Mat]): Flow[In, (Out, Out2), NotUsed] = {
Flow.fromGraph(GraphDSL.create() { implicit b =>
import GraphDSL.Implicits._
val broadcast = b.add(Broadcast[In](2))
val zip = b.add(Zip[Out, Out2])
broadcast.out(0) -> zip.in0
broadcast.out(1) -> zip.in1
FlowShape(broadcast.in, zip.out)
})
}
}
用法val c: Flow[Input, (Data, Unit), NotUsed] = a.zipFlow(b)