簡體   English   中英

Akka流通過異步處理在廣播中產生背壓

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

我很難理解akka-stream在進行廣播時,如果一個分支的廣播占用了圖表中的大量時間(異步),則akka-stream是否會對Source施加反壓。

我嘗試了bufferbatch以查看是否在源上施加了任何反壓,但它看起來並不像它。 我也嘗試了刷新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()
}

我希望在運行程序時在控制台中看到“ Batched ...”(批處理...),並且由於f4的處理速度不夠快而使它暫時卡住。 目前,由於連續生成數字並且不進行批處理,因此這些行為均未達到預期的效果。

編輯:我注意到一段時間后,批處理消息開始在控制台中打印出來。 我仍然不知道為什么它不會很快發生,因為第一個元素應該發生背壓

解釋此行為的原因是由akka在設置異步邊界時引入的內部緩沖區。

異步運算符的緩沖區

內部緩沖區,在使用異步運算符時作為優化引入。


雖然流水線通常會提高吞吐量,但實際上,將元素傳遞到異步(並因此穿過線程)邊界是很昂貴的。 為了攤銷此成本,Akka Streams在內部使用了窗口化的批量反壓策略。 之所以要窗口化,是因為與Stop-And-Wait協議相反,多個元素可能與請求元素同時“進行中”。 這也是批處理的,因為一旦從窗口緩沖區中刪除了一個元素,就不會立即請求一個新元素,而在多個元素被耗盡之后,會請求多個元素 這種批處理策略減少了通過異步邊界傳播背壓信號的通信成本。

我知道這是玩具流,但是如果您解釋目標是什么,我會盡力幫助您。

您需要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