让我们举一个非常简单的例子:
Source(1 to 10)
.alsoTo(Sink.foreach(v => println(s"each: $v")))
.toMat(Sink.head)(Keep.right)
.run()
根据alsoTo
文档,我希望Sink.foreach
打印所有元素,但是,它只会首先打印。如果我切换Sink.foreach
和Sink.head
的位置,也会发生同样的情况。
但是,如果广播是通过GraphDSL
实现的,则即使接收器之一是Sink.head
,整个源也会被消耗。
编辑:
alsoTo
的文档规定以下内容:
将给定的接收器附加到此流,这意味着通过此流的元素也将发送到接收器。
在我看来,这听起来像是广播,但这也许是我犯错的地方。我还可以解释为toMat
控制流程。因此,我希望以下内容可以从源中获取所有元素:
Source(1 to 10)
.alsoTo(Sink.head)
.toMat(Sink.seq)(Keep.right)
.run()
GraphDSL版本可以正常工作:
val s1 = Sink.foreach[Int](v => println(s"each: $v"))
val s2 = Sink.head[Int]
val source = Source(1 to 10)
RunnableGraph.fromGraph(GraphDSL.create(s1, s2)((_, _)) { implicit builder => (s1, s2) =>
import GraphDSL.Implicits._
val broadcast = builder.add(Broadcast[Int](2))
source ~> broadcast.in
broadcast.out(0) ~> s1
broadcast.out(1) ~> s2
ClosedShape
}).run()
答案 0 :(得分:1)
原因是Sink.head
使用单个元素并完成自身。它以cancel
的形式向上游传播,并且此后没有源发送任何元素。
akka.stream.impl.HeadOptionStage.onPush
中的代码显示了它
def onPush(): Unit = {
p.trySuccess(Option(grab(in)))
completeStage()
}
completeStage
在所有已调用的输入或输出端口上自动调用[[cancel()]]或[[complete()]],然后将操作符标记为已停止。
更新
alsoTo
是使用以下参数配置的广播:
val bcast = b.add(Broadcast[Out](2, eagerCancel = true))
您的GraphDSL
版本的工作方式有所不同,因为默认情况下,广播为eagerCancel = false
。
eagerCancel
如果为true,则广播的任何下游取消都会取消上游广播。