Akka streams pass through flow limiting Parallelism / throughput of processing flow
我有一个用例,我想向外部系统发送消息,但是发送此消息的流程采用并返回我不能在下游使用的类型。对于通过流程来说,这是一个很好的用例。我在这里使用实现。最初,我担心如果processingFlow使用mapAsyncUnordered,那么该流程将无法正常工作。由于处理流程可能会重新排序消息,并且zip可能会推出带有不正确对的元组。例如,在以下示例中。
1 2 3 4 5 6 7 8 |
我希望处理流程可以相对于其输入重新排序其输出,并且我将得到如下结果:
1 | [(30,1), (40,2),(10,3),(10,4), ...] |
右边(通过的对象总是按顺序排列),但穿过我的mapAsyncUnordered的左边可能与不正确的元素结合在一起,形成一个错误的元组。
实际上我得到了:
1 | [(10,1), (20,2),(30,3),(40,4), ...] |
每次。经过进一步调查,我发现代码运行缓慢,尽管我的地图异步无序,但实际上根本没有并行运行。我尝试在异步边界之前和之后引入一个缓冲区,但是它似乎总是按顺序运行。这解释了为什么总是订购但我希望我的处理流程具有更高的吞吐量。
我想出了以下解决方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | object PassThroughFlow { def keepRight[A, A1](processingFlow: Flow[A, A1, NotUsed]): Flow[A, A, NotUsed] = keepBoth[A, A1](processingFlow).map(_._2) def keepBoth[A, A1](processingFlow: Flow[A, A1, NotUsed]): Flow[A, (A1, A), NotUsed] = Flow.fromGraph(GraphDSL.create() { implicit builder => { import GraphDSL.Implicits._ val broadcast = builder.add(Broadcast[A](2)) val zip = builder.add(ZipWith[A1, A, (A1, A)]((left, right) => (left, right))) broadcast.out(0) ~> processingFlow ~> zip.in0 broadcast.out(1) ~> zip.in1 FlowShape(broadcast.in, zip.out) } }) } object ParallelPassThroughFlow { def keepRight[A, A1](parallelism: Int, processingFlow: Flow[A, A1, NotUsed]): Flow[A, A, NotUsed] = keepBoth(parallelism, processingFlow).map(_._2) def keepBoth[A, A1](parallelism: Int, processingFlow: Flow[A, A1, NotUsed]): Flow[A, (A1, A), NotUsed] = { Flow.fromGraph(GraphDSL.create() { implicit builder => import GraphDSL.Implicits._ val fanOut = builder.add(Balance[A](outputPorts = parallelism)) val merger = builder.add(Merge[(A1, A)](inputPorts = parallelism, eagerComplete = false)) Range(0, parallelism).foreach { n => val passThrough = PassThroughFlow.keepBoth(processingFlow) fanOut.out(n) ~> passThrough ~> merger.in(n) } FlowShape(fanOut.in, merger.out) }) } } |
两个问题:
您所看到的行为是
1 2 | broadcast.out(0) ~> processingFlow ~> zip.in0 broadcast.out(1) ~> zip.in1 |
考虑上图中第一个元素(
至于您的