使用失败溢出策略时,Akka流Source.queue挂起

时间:2017-05-26 12:49:07

标签: akka akka-stream

以下Scala代码段似乎没有返回:

val queue = 
  Source.queue[Unit](10, OverflowStrategy.fail)
    .throttle(1, 1 second, 1, ThrottleMode.shaping)
    .to(Sink.ignore)
    .run()

Await.result(
  (1 to 15).map(_ => queue.offer(())).last,
  Duration.Inf)

这是Akka流中的错误还是我做错了什么?

编辑:回过头来看,这个错误在Akka中被打开并被接受:https://github.com/akka/akka/issues/23078

1 个答案:

答案 0 :(得分:1)

该计划让您更深入地了解这里发生的事情:

import akka.actor.ActorSystem
import akka.stream.scaladsl.{Keep, Sink, Source}
import akka.stream.{ActorMaterializer, OverflowStrategy, ThrottleMode}

import scala.concurrent.Await
import scala.concurrent.duration._

object Test extends App {
  implicit val actorSystem = ActorSystem()
  implicit val materializer = ActorMaterializer()
  import actorSystem.dispatcher

  val (queue, finalFuture) =
    Source.queue[Unit](10, OverflowStrategy.fail)
      .map(_ => println("Before throttle"))
      .throttle(1, 1.second, 1, ThrottleMode.shaping)
      .map(_ => println("After throttle"))
      .toMat(Sink.ignore)(Keep.both)
      .run()

  finalFuture.onComplete(r => println(s"Materialized future from ignore completed: $r"))

  Await.result((1 to 25).map(_ => queue.offer(()).map(e => println(s"Offer result: $e"))).last, Duration.Inf)
}

它为我打印以下内容:

Offer result: Enqueued
After throttle
Before throttle
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Failure(akka.stream.BufferOverflowException: Buffer overflow (max capacity was: 10)!)
Materialized future from ignore completed: Failure(akka.stream.BufferOverflowException: Buffer overflow (max capacity was: 10)!)

但是有时会以异常结束:

Before throttle
After throttle
Before throttle
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Enqueued
Offer result: Failure(akka.stream.BufferOverflowException: Buffer overflow (max capacity was: 10)!)
Materialized future from ignore completed: Failure(akka.stream.BufferOverflowException: Buffer overflow (max capacity was: 10)!)
Exception in thread "main" java.lang.IllegalStateException: Stream is terminated. SourceQueue is detached
    at akka.stream.impl.QueueSource$$anon$1$$anonfun$postStop$1.apply(Sources.scala:57)
    at akka.stream.impl.QueueSource$$anon$1$$anonfun$postStop$1.apply(Sources.scala:56)
    at akka.stream.stage.CallbackWrapper$$anonfun$invoke$1.apply$mcV$sp(GraphStage.scala:1373)
    at akka.stream.stage.CallbackWrapper$class.akka$stream$stage$CallbackWrapper$$locked(GraphStage.scala:1379)
    at akka.stream.stage.CallbackWrapper$class.invoke(GraphStage.scala:1369)
    at akka.stream.impl.QueueSource$$anon$1.invoke(Sources.scala:47)
    at akka.stream.impl.QueueSource$$anon$2.offer(Sources.scala:180)
    at test.Test$$anonfun$4.apply(Test.scala:25)
    at test.Test$$anonfun$4.apply(Test.scala:25)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
    at scala.collection.immutable.Range.foreach(Range.scala:160)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:234)
    at scala.collection.AbstractTraversable.map(Traversable.scala:104)
    at test.Test$.delayedEndpoint$test$Test$1(Test.scala:25)
    at test.Test$delayedInit$body.apply(Test.scala:10)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:34)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App$$anonfun$main$1.apply(App.scala:76)
    at scala.App$$anonfun$main$1.apply(App.scala:76)
    at scala.collection.immutable.List.foreach(List.scala:381)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35)
    at scala.App$class.main(App.scala:76)

也就是说,你看到并发行动 - 你提交的期货是并行执行的,其中一个完成失败,但更常见的是他们只是挂起。如果你按照这样的顺序得到失败的未来,那么你会得到一个例外,否则你会得到一个无限的等待。

要确定您的流已实际终止,您必须直接查看它,就像上面所做的那样。但最重要的是,您最好不要将配置的事件数量推送到队列中,或者如果您确实希望这样做而使用OverflowStrategy.backpressure,则始终需要等待您提交的最后一个未来在执行下一个offer()之前完成。