[英]How do you deal with futures and mapAsync in Akka Flow?
我构建了一个定义简单流程的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? 或者......谢谢
对不起,我是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()
正如您已经想到的那样,替换:
mapAsync(i => Future{i + delta})
有:
map(_ + delta).async
在这两个流程中将实现你想要的。
不同的结果归结为mapAsync
和map + async
之间的关键区别。 虽然mapAsync
允许在并行线程中执行Futures,但是多个mapAsync
流阶段仍然由相同的底层actor执行,这将在执行之前执行运算符融合 (通常为了成本效率)。
另一方面, async
实际上在流流中引入了异步边界,各个流阶段由不同的actor处理。 在您的情况下,两个流动阶段中的每一个独立地向下游发射元素,并且首先消耗的元素首先被消耗。 不可避免地需要跨异步边界管理流的成本,Akka Stream使用窗口缓冲策略来摊销成本(参见Akka Stream doc )。
有关详细信息: mapAsync
和async
之间的mapAsync
,此博客文章可能会引起关注。
所以你试图将f2和f4的结果连接在一起。 在这种情况下,您正在尝试执行有时称为“分散聚集模式”的操作。
我不认为有现成的方法来实现它,没有添加一个自定义的有状态阶段,将跟踪f2和f4的输出,并在两者都可用时发出记录。 但是要记住它们是一些复杂问题:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.