akka stream asyncBoundary vs mapAsync

时间:2017-11-15 07:10:03

标签: scala akka akka-stream stream-processing

我想了解asyncBoundarymapAsync之间的区别。从一目了然,我猜他们应该是一样的。但是,当我运行代码时,asyncBoundary的效果似乎比mapAsync

更快

这是代码

implicit val system = ActorSystem("sourceDemo")
implicit val materializer = ActorMaterializer()


Source(1 to 100).mapAsync(100)(t => Future {t + 1}).mapAsync(100)(t => Future {t * 2}).map(println).to(Sink.ignore).run()
Source(1 to 100).map(_ + 1).withAttributes(Attributes.asyncBoundary).map(_ * 2).map(t => println("async boundary", t)).to(Sink.ignore).run()

输出: 异步边界总是比mayAsync更快完成。

从描述的有关asyncBoundary(https://doc.akka.io/docs/akka-stream-and-http-experimental/current/scala/stream-flows-and-basics.html)的文档中,我可以看到它在不同的CPU上运行,但mapAsync是使用Future的多线程。未来也是异步的。

请问有关这两个API的更多说明吗?

1 个答案:

答案 0 :(得分:10)

<强>异步

正如您正确指出的那样,强制在两个阶段之间插入异步边界。在你的例子中

)

这实际上意味着Source(1 to 100).map(_ + 1).withAttributes(Attributes.asyncBoundary).map(_ * 2).map(t => println("async boundary", t)).to(Sink.ignore).run() 操作和+ 1操作将由分离的actor运行。这样可以实现流水线操作,同时元素移动到* 2阶段,同时可以为* 2阶段引入另一个元素。如果您强制执行异步边界,则同一个actor会对操作进行顺序操作,并在从上游请求新操作之前对一个元素执行操作。

顺便说一下,您的示例可以使用+ 1组合器以较短的格式重写:

async

<强> mapAsync

这是并行执行异步操作的阶段。并行因子允许您指定要旋转以提供传入元素的最大并行actor数。并行计算的结果由Source(1 to 100).map(_ + 1).async.map(_ * 2).map(t => println("async boundary", t)).to(Sink.ignore).run() 阶段按顺序按顺序进行跟踪和发布。

在你的例子中

mapAsync

可能最多100个Source(1 to 100).mapAsync(100)(t => Future {t + 1}).mapAsync(100)(t => Future {t * 2}).map(println).to(Sink.ignore).run() 个操作(即所有操作)可以并行运行,并按顺序收集结果。随后,最多可以并行运行100个+ 1个操作,并且结果再次按顺序收集并向下游发送。

在您的示例中,您正在运行CPU限制的快速操作,这些操作无法使用* 2,因为此阶段所需的基础设施很可能比运行其中100个这样的操作的优势要昂贵得多。平行。 mapAsync在处理IO绑定,慢速操作时非常有用,并行化非常方便。

另请注意,mapAsync也可以根据定义引入异步边界,因此您可能会将其视为mapAsync的“扩展”,您可以在其中指定大于1的并行度。