简体   繁体   English

Akka流通过异步处理在广播中产生背压

[英]Akka-streams backpressure on broadcast with async processing

I am struggling with understanding if akka-stream enforces backpressure on Source when having a broadcast with one branch taking a lot of time (asynchronous) in the graph. 我很难理解akka-stream在进行广播时,如果一个分支的广播占用了图表中的大量时间(异步),则akka-stream是否会对Source施加反压。

I tried buffer and batch to see if there was any backpressure applied on the source but it does not look like it. 我尝试了bufferbatch以查看是否在源上施加了任何反压,但它看起来并不像它。 I also tried flushing System.out but it does not change anything. 我也尝试了刷新System.out但它没有任何改变。

object Test extends App {
/* Necessary for akka stream */
implicit val system = ActorSystem("test")
implicit val materializer: ActorMaterializer = ActorMaterializer()

val g = RunnableGraph.fromGraph(GraphDSL.create() { implicit builder: GraphDSL.Builder[NotUsed] =>
    import GraphDSL.Implicits._

    val in = Source.tick(0 seconds, 1 seconds, 1)
        in.runForeach(i => println("Produced " + i))

    val out = Sink.foreach(println)
    val out2 = Sink.foreach[Int]{ o => println(s"2 $o") }

    val bcast = builder.add(Broadcast[Int](2))

    val batchedIn: Source[Int, Cancellable] = in.batch(4, identity) {
        case (s, v) => println(s"Batched ${s+v}"); s + v
    }

    val f2 = Flow[Int].map(_ + 10)
    val f4 = Flow[Int].map { i => Thread.sleep(2000); i}

    batchedIn ~> bcast ~> f2 ~> out
                 bcast ~> f4.async ~> out2
    ClosedShape
})

g.run()
}

I would expect to see "Batched ..." in the console when I am running the program and at some point to have it momentarily stuck because f4 is not fast enough to process the values. 我希望在运行程序时在控制台中看到“ Batched ...”(批处理...),并且由于f4的处理速度不够快而使它暂时卡住。 At the moment, none of those behave as expected as the numbers are generated continuously and no batch is done. 目前,由于连续生成数字并且不进行批处理,因此这些行为均未达到预期的效果。

EDIT: I noticed that after some time, the batch messages start to print out in the console. 编辑:我注意到一段时间后,批处理消息开始在控制台中打印出来。 I still don't know why it does not happen sooner as the backpressure should happen for the first elements 我仍然不知道为什么它不会很快发生,因为第一个元素应该发生背压

The reason that explains this behavior are internal buffers that are introduced by akka when async boundaries are set. 解释此行为的原因是由akka在设置异步边界时引入的内部缓冲区。

Buffers for asynchronous operators 异步运算符的缓冲区

internal buffers that are introduced as an optimization when using asynchronous operators. 内部缓冲区,在使用异步运算符时作为优化引入。


While pipelining in general increases throughput, in practice there is a cost of passing an element through the asynchronous (and therefore thread crossing) boundary which is significant. 虽然流水线通常会提高吞吐量,但实际上,将元素传递到异步(并因此穿过线程)边界是很昂贵的。 To amortize this cost Akka Streams uses a windowed, batching backpressure strategy internally. 为了摊销此成本,Akka Streams在内部使用了窗口化的批量反压策略。 It is windowed because as opposed to a Stop-And-Wait protocol multiple elements might be “in-flight” concurrently with requests for elements. 之所以要窗口化,是因为与Stop-And-Wait协议相反,多个元素可能与请求元素同时“进行中”。 It is also batching because a new element is not immediately requested once an element has been drained from the window-buffer but multiple elements are requested after multiple elements have been drained . 这也是批处理的,因为一旦从窗口缓冲区中删除了一个元素,就不会立即请求一个新元素,而在多个元素被耗尽之后,会请求多个元素 This batching strategy reduces the communication cost of propagating the backpressure signal through the asynchronous boundary. 这种批处理策略减少了通过异步边界传播背压信号的通信成本。

I understand that this is a toy stream, but if you explain what is your goal I will try to help you. 我知道这是玩具流,但是如果您解释目标是什么,我会尽力帮助您。

You need mapAsync instead of async 您需要mapAsync而不是async

val g = RunnableGraph.fromGraph(GraphDSL.create() { implicit builder: GraphDSL.Builder[NotUsed] =>
  import akka.stream.scaladsl.GraphDSL.Implicits._

  val in = Source.tick(0 seconds, 1 seconds, 1).map(x => {println(s"Produced ${x}"); x})

  val out = Sink.foreach[Int]{ o => println(s"F2 processed $o") }
  val out2 = Sink.foreach[Int]{ o => println(s"F4 processed $o") }

  val bcast = builder.add(Broadcast[Int](2))

  val batchedIn: Source[Int, Cancellable] = in.buffer(4,OverflowStrategy.backpressure)

  val f2 = Flow[Int].map(_ + 10)
  val f4 = Flow[Int].mapAsync(1) { i => Future { println("F4 Started Processing"); Thread.sleep(2000); i }(system.dispatcher) }

  batchedIn ~> bcast ~> f2 ~> out
  bcast ~> f4 ~> out2
  ClosedShape
}).run()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM