我想了解asyncBoundary
和mapAsync
之间的区别。从一目了然,我猜他们应该是一样的。但是,当我运行代码时,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的更多说明吗?
答案 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的并行度。