为什么Keep.None不影响Akka流执行结果?

时间:2017-05-17 14:52:17

标签: scala f# akka-stream

我有一个简单的Akka Streams测试代码(用F#编写,但Scala版本不匹配):

    var source = Source.From(Enumerable.Range(1, 3));
    var flow = Flow.FromFunction(new Func<int, string>(x => (x * 2).ToString()));
    var sink = Sink.ForEach<string>(output.Add);
    var runnable = source.Via(flow).To(sink);

由于Via helper方法只是ViaMaterialized(flow,Keep.Left)的快捷方式,我可以像这样重写代码:

var source = Source.From(Enumerable.Range(1, 3));
var flow = Flow.FromFunction(new Func<int, string>(x => (x * 2).ToString()));
var sink = Sink.ForEach<string>(output.Add);
var runnable = source.ViaMaterialized(flow, Keep.Left).To(sink);

Keep属性(Left,Right,Both或None)告诉流实现器应该保留流操作的指定端的值。但是我注意到如果我将Keep.Left改为Keep.Right,Keep.Both或事件Keep.None,那就不会改变执行结果中的任何内容:接收器将始终根据流转换函数接收输出

我认为在流图中为Flow阶段使用非None Keep值是必要的,以确保将值发送到接收器。我一定是误解了这个的含义,所以我的问题是为什么即使双方的物化都被禁用,流动也会起作用?你可以举一个例子,当改变Left,Right,Both和None之间的Keep值会影响到达接收器的值吗?

2 个答案:

答案 0 :(得分:4)

Keep.*函数不会影响实现过程本身,只会影响您的实现过程。

更具体地说,在实现时(即调用run()时),流的每个阶段(在您的示例中,源,流和接收器)将始终实现 - 因此在引擎盖下产生物化价值。您可以清楚地看到它们的最后一个类型参数的值。

为了方便用户,您很可能不会对所有这些内容感兴趣,因此您可以相应地使用Keep.*来选择保留的内容。这直接反映了run()的返回类型。

答案 1 :(得分:4)

您会混淆一个流实现的事实以及它具有物化价值的事实。

流(或更一般地说,图表)是流的蓝图。在可运行图上使用run()方法时,将使用此蓝图实现流。此流可以完成预期的任何操作,而无需考虑具体化的值。

什么是物化价值?使用方法run()时,将返回一个值。这是您的流的物化价值。大多数情况下(对于简单的内置阶段),物化值并不重要(在scala中称为NotUsed,我不了解.NET)。一个非常重要的例子是Sink.ignore,其实现为Future[Done]。它为您提供了一个句柄,说明您实现的特定流何时将完全消耗其输入(或抛出错误)。更一般地说,具体化的价值为你提供了关于你的流中的内容的一些间接信息(对于这个陈述的模糊性感到遗憾,但是手头的原则对于我来说太笼统了以至于更加明确)。

在构建图形时,您将不同的部分放在一起,这些部分都具有不同的具体化值。由于您的可运行图只能有一个,因此需要以某种方式组合它们。 Keep.{right, left, both, none}是简单的函数,它们通过仅保留其中一个值或两者或无值来组合这些值。但是,即使您决定不保留它们,也不会改变这两个图形将被实现并生成的事实。