如何并排组合两个流程?

时间:2017-11-18 15:40:19

标签: scala functional-programming akka akka-stream

是否有一个Akka stream combinator用于执行以下操作(或者其他类似的操作)? (现在让我们称之为and。)

(flow1: Flow[I, O, Mat]).and[O2](flow2: Flow[I, O2, Mat]): Flow[I, (O, O2), Mat]

语义是无论来源如何,它的元素都将被传递给Flow,并且它们的输出将被组合成一个新的Flow作为元组。 (对于那些熟悉类别理论风格函数式编程的箭头的人,我正在寻找像&&&这样的东西。)

图书馆中有两个看似相关的组合器,即zipalsoTo。但前者接受SourceShape,后者接受SinkShape。也不会承认GraphShape。为什么会这样?

我的用例如下:

someSource
  .via(someFlowThatReturnsUnit.and(Flow.apply))
  .runWith(someSink)

找不到像.and这样的内容,我修改了我原来的Flow

someSource
  .via(someFlowThatDoesWhateverItWasDoingEarlierButNowAlsoEmitsInputsAsIs)
  .runWith(someSink)

这有效,但我正在寻找一种更清洁,更具成分性的解决方案。

1 个答案:

答案 0 :(得分:6)

<强>通知

正如Viktor Klang在评论中指出的那样:只有知道两个流量Tuple2[O,O2]&amp; flow1,相对于输入元素计数和输出元素计数是1:1。

基于图表的解决方案

可以在Graph内创建元组构造。事实上,您的问题几乎完全符合介绍性示例:

enter image description here

在链接中扩展示例代码,您可以使用BroadcastZip

flow2

有些Hacky Stream解决方案

如果您正在寻找纯流解决方案,可以使用中间流,但不会保留val g = RunnableGraph.fromGraph(GraphDSL.create() { implicit builder: GraphDSL.Builder[NotUsed] => import GraphDSL.Implicits._ val in = Source(1 to 10) val out = Sink.ignore val bcast = builder.add(Broadcast[Int](2)) val merge = builder.add(Zip[Int, Int]()) //different than link val f1, f2, f4 = Flow[Int].map(_ + 10) val f3 = Flow[(Int, Int)].map(t => t._1 + t._2) //different than link in ~> f1 ~> bcast ~> f2 ~> merge ~> f3 ~> out bcast ~> f4 ~> merge ClosedShape })//end RunnableGraph.fromGraph ,并且它涉及为每个输入元素实现2个流:

Mat

通用拉链

如果你想使这个压缩通用,对于大多数流,那么输出类型必须是def andFlows[I, O, O2] (maxConcurrentSreams : Int) (flow1: Flow[I, O, NotUsed], flow2: Flow[I, O2, NotUsed]) (implicit mat : Materializer, ec : ExecutionContext) : Flow[I, (O, O2), _] = Flow[I].mapAsync(maxConcurrentStreams){ i => val o : Future[O] = Source .single(i) .via(flow1) .to(Sink.head[O]) .run() val o2 : Future[O2] = Source .single(i) .via(flow2) .to(Sink.head[O2]) .run() o zip o2 }//end Flow[I].mapAsync 。在上述(Seq[O], Seq[O2])函数中使用Sink.seq代替Sink.head可以生成此类型:

andFlows