工人池与Akka Streams

时间:2016-03-25 13:13:18

标签: scala akka akka-stream

akka streams documentation中所述,我试图建立一个工人池(流程):

def balancer[In, Out](worker: Flow[In, Out, NotUsed], workerCount: Int): Flow[In, Out, NotUsed] = {
    import GraphDSL.Implicits._

    Flow.fromGraph(GraphDSL.create() { implicit b =>
      val balancer = b.add(Balance[In](workerCount))
      val merge = b.add(Merge[Out](workerCount))

      for (_ <- 1 to workerCount) {
        balancer ~> worker ~> merge
      }
      FlowShape(balancer.in, merge.out)
    })
  }

然后我用这个函数并行运行流程:

def main(args: Array[String]) {
    val system = ActorSystem()
    implicit val mat = ActorMaterializer.create(system)

    val flow = Flow[Int].map(e => {
      println(e)
      Thread.sleep(1000) // 1 second
      e
    })

    Source(Range.apply(1, 10).toList)
      .via(balancer(flow, 3))
      .runForeach(e => {})
  }

我得到预期的输出1, 2, 3, 4, 5, 6, 7, 8, 9,但数字以每秒1的速率出现(没有并行性)。我做错了什么?

2 个答案:

答案 0 :(得分:3)

该部分的文档已过时,将在下一版本中修复。基本上你只需要在流程本身上调用.async。通过这样做,你可以在流程周围绘制一个“盒子”(你可以想象它是一个带有一个输入和输出端口的盒子),它可以防止在那个盒子上融合。通过这样做,基本上所有工人都将成为专职演员。图的其余部分(广播和合并阶段)将共享另一个actor(它们不会在不同的actor上运行,异步盒只保护流,外部的东西仍然会被融合)。

答案 1 :(得分:1)

正如Endre Varga指出的那样,流本身应标有.async

但即使这样,行为也不确定,因为异步阶段的默认缓冲区大小为16,而平衡器可能会将所有消息发送给同一个工作人员。

因此,balancer ~> worker.async.addAttributes(Attributes.inputBuffer(1, 1)) ~> merge会导致所需的行为。

对于项目成员给出的答案,请参阅: https://github.com/akka/akka/issues/20146#issuecomment-201381356