【问题标题】:akka stream materialized value using different optionsakka 流物化值使用不同的选项
【发布时间】:2020-12-22 07:42:45
【问题描述】:

我是 akka 流的新手,想了解流中的物化是如何工作的

//Print sum of elements from 1 to 10
val newSource = Source(1 to 10)
val flow      = Flow[Int].fold(0)((a, b) => a + b)
val sink      = Sink.foreach(println)
val sumFuture = newSource.via(flow).toMat(sink)(Keep.left).run()

它使用Keep.leftKeep.right 打印值55。两者有何不同?

我想探索看看Keep.leftKeep.right 给出不同的值以及我们如何使用Keep.both

【问题讨论】:

标签: scala akka akka-stream


【解决方案1】:

物化值可以由汇和源产生。通过将源与接收器组合来创建可运行图。 Keep 定义组合时要保留的具体化值

  • Keep.right 选择接收器的具体化值
  • Keep.left 选择源的具体化值
  • Keep.both 以元组的形式选择两者
  • Keep.none 忽略两者并选择NotUsed,即表示没有具体化值的标记。

默认情况下,Keep.left 用于viato 等操作。

以下示例突出显示了这一点

给定一个Source[Int, String] 和一个Sink[Int, Future[Int]]

val source: Source[Int, String] = Source(List(1, 2, 3)).mapMaterializedValue(_ => "Source Mat Value")
val sink: Sink[Int, Future[Int]] = Sink.fold(0)(_ + _)

我们可以结合sourcesink 来创建具有不同物化值的可运行图。

val left: String = source.to(sink).run() //same as toMat(...)(Keep.left)
val right: Future[Int] = source.toMat(sink)(Keep.right).run()
val both: (String, Future[Int]) = source.toMat(sink)(Keep.both).run()

现在,如果我们运行它并打印它产生的每个物化值

left=Source Mat Value
right=Future(Success(6))
both=(Source Mat Value,Future(Success(6)))

请不要将物化值与流元素的处理混为一谈。

考虑关注fold 阶段

val flowFold: Flow[Int, Int, NotUsed] = Flow[Int].fold(0)(_ + _)
val sinkFold: Sink[Int, Future[Int]] = Sink.fold(0)(_ + _)

flowFoldfold 函数应用于流中的每个元素,并将表示fold 结果的一个 值推送到下游。如果需要,可以进一步处理此元素。

sinkFold 是图中的最后阶段,它不能将元素推向下游。当graph处理完所有元素并完成时,它使用物化值Future[Int]返回fold结果。

如果Flow.fold 的值为55,这应该是流的具体化值而不是NotUsed

不,值55 不是物化值。它作为一个元素被推送到下游接收器。

您可以在Sink.head 的帮助下将55 元素“捕获”到具体化的值中

val flow: Flow[Int, Int, NotUsed] = Flow[Int].fold(0)(_ + _)
val alternativeFoldSink: Sink[Int, Future[Int]] = flow.toMat(Sink.head)(Keep.right)

每个阶段都可以产生物化价值然后(为什么)不能Flow.fold 产生物化的价值。

是的,每个阶段都可能产生物化价值。但是Flow.fold 的设计目的是不这样做。大多数Flow 定义不提供具体化值。如果你想使用物化值和fold,我建议使用Sink.fold

【讨论】:

  • 我试过val newSource = Source(1 to 10) val flow = Flow[Int].fold(0)((a, b) => a + b) val sumFuture1 = newSource.viaMat(flow)(Keep.right).toMat(Sink.ignore)(Keep.both).run() sumFuture1._2.onComplete { case Success(value) => println(s"The sum is :$value") case Failure(ex) => println(s"The sum cannot be computed :$ex") } 输出是总和:完成,它不打印总和。我想从 Keep.right 和 Keep.left 发出一些整数值,你能举个例子吗UniqueKillSwitch 有点复杂
  • 源没有我们使用过的物化值`mapMaterialisedValue
  • @coder25 是的,Source(List(...)) 的具体化值为 NotUsed。因此使用mapMaterializedValue 具有物化值
  • 为什么我们用 Sink.fold 替换 Flow 不提供物化价值
  • 如果 Flow.fold 的值为 55,如果这是流的物化值而不是 NotUsed,请明确说明。这是否意味着我们永远无法从 Flow 中获得物化值
【解决方案2】:

重要的是要记住一个流阶段可以有

  • 一个物化值,它在流被物化时创建,在任何元素通过该物化之前。因此,它不能依赖于通过/进入/流出流的值。

  • 在流运行时传递到流的下一个阶段的零个或多个输出值。

每个阶段都有一个物化的价值。每个不是接收器的阶段都可能具有输出值。对于源,一般来说,物化值提供了一些影响流行为的方法(例如,Source.actorRef 的物化值是一个ActorRef,它允许您通过向ActorRef 发送消息来将元素推送到流中,或者 Alpakka Kafka 中的各种 Kafka 消费者源允许您停止从 Kafka 消费,而无需停止流,直到流耗尽为止。

对于接收器,一般来说,从接收器中获取值的唯一方法是通过物化值(因为没有输出)。由于必须在任何数据流经流之前创建具体化值,这就是为什么大多数接收器具体化为 Future(尚不可用的数据的占位符)并且通常不会完成该值,直到流完成(因为Futures 最多写入一次)。

每个阶段都有一个物化值,但不是每个阶段都有一个有意义的物化值:对于那些,特殊的NotUsed 值(单例)编码“没有意义”。大多数流程阶段都属于这一类:它们的存在只是为了将输入转换为输出。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-27
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-18
    相关资源
    最近更新 更多