我建立了一个定义简单流程的akka图DSL。但是流程f4花费3秒发送元素,而f2花费10秒。
结果,我得到了:3、2、3、2。但是,这不是我想要的。由于f2需要太多时间,我想得到:3、3、2、2。这是代码...
implicit val actorSystem = ActorSystem("NumberSystem")
implicit val materializer = ActorMaterializer()
val g = RunnableGraph.fromGraph(GraphDSL.create() { implicit builder: GraphDSL.Builder[NotUsed] =>
import GraphDSL.Implicits._
val in = Source(List(1, 1))
val out = Sink.foreach(println)
val bcast = builder.add(Broadcast[Int](2))
val merge = builder.add(Merge[Int](2))
val yourMapper: Int => Future[Int] = (i: Int) => Future(i + 1)
val yourMapper2: Int => Future[Int] = (i: Int) => Future(i + 2)
val f1, f3 = Flow[Int]
val f2= Flow[Int].throttle(1, 10.second, 0, ThrottleMode.Shaping).mapAsync[Int](2)(yourMapper)
val f4= Flow[Int].throttle(1, 3.second, 0, ThrottleMode.Shaping).mapAsync[Int](2)(yourMapper2)
in ~> f1 ~> bcast ~> f2 ~> merge ~> f3 ~> out
bcast ~> f4 ~> merge
ClosedShape
})
g.run()
那么我要去哪里错了?与future或mapAsync吗?要不然 ... 谢谢
答案 0 :(得分:1)
对不起,我是akka的新手,所以我还在学习。为了获得预期的结果,一种方法是放置异步:
val g = RunnableGraph.fromGraph(GraphDSL.create() { implicit builder: GraphDSL.Builder[NotUsed] =>
import GraphDSL.Implicits._
val in = Source(List(1, 1))
val out = Sink.foreach(println)
val bcast = builder.add(Broadcast[Int](2))
val merge = builder.add(Merge[Int](2))
val yourMapper: Int => Future[Int] = (i: Int) => Future(i + 1)
val yourMapper2: Int => Future[Int] = (i: Int) => Future(i + 2)
val f1, f3 = Flow[Int]
val f2= Flow[Int].throttle(1, 10.second, 0, ThrottleMode.Shaping).map(_+1)
//.mapAsyncUnordered[Int](2)(yourMapper)
val f4= Flow[Int].throttle(1, 3.second, 0, ThrottleMode.Shaping).map(_+2)
//.mapAsync[Int](2)(yourMapper2)
in ~> f1 ~> bcast ~> f2.async ~> merge ~> f3 ~> out
bcast ~> f4.async ~> merge
ClosedShape
})
g.run()
答案 1 :(得分:1)
您已经知道了,替换:
mapAsync(i => Future{i + delta})
具有:
map(_ + delta).async
在两个流程中将实现您想要的。
不同的结果归结为mapAsync
和map + async
之间的关键区别。尽管mapAsync
允许在并行线程中执行Future,但是多个mapAsync
流程阶段仍由同一基础参与者管理,该参与者将在执行之前执行operator fusion(通常是为了节省成本)
另一方面,async
实际上将异步边界引入到流中,其中各个流阶段由单独的参与者处理。在您的情况下,两个流阶段中的每一个都独立地向下游排放元素,而首先排放的元素首先被消耗。不可避免地,跨异步边界管理流会产生成本,并且Akka Stream使用窗口缓冲策略来分摊成本(请参见此Akka Stream doc)。
有关更多详细信息,请注意:mapAsync
和async
之间的区别,此blog post可能很有趣。
答案 2 :(得分:0)
因此,您尝试将来自f2和f4的结果合并在一起。在这种情况下,您将尝试执行有时称为“散布收集模式”的操作。
我认为没有现成的方法可以实现它,而无需添加自定义的有状态阶段,该阶段将跟踪f2和f4的输出并在两者可用时发出记录。但是要牢记一些复杂之处: