我有一个简单的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值会影响到达接收器的值吗?
答案 0 :(得分:4)
Keep.*
函数不会影响实现过程本身,只会影响您的实现过程。
更具体地说,在实现时(即调用run()
时),流的每个阶段(在您的示例中,源,流和接收器)将始终实现 - 因此在引擎盖下产生物化价值。您可以清楚地看到它们的最后一个类型参数的值。
为了方便用户,您很可能不会对所有这些内容感兴趣,因此您可以相应地使用Keep.*
来选择保留的内容。这直接反映了run()
的返回类型。
答案 1 :(得分:4)
您会混淆一个流实现的事实以及它具有物化价值的事实。
流(或更一般地说,图表)是流的蓝图。在可运行图上使用run()
方法时,将使用此蓝图实现流。此流可以完成预期的任何操作,而无需考虑具体化的值。
什么是物化价值?使用方法run()
时,将返回一个值。这是您的流的物化价值。大多数情况下(对于简单的内置阶段),物化值并不重要(在scala中称为NotUsed
,我不了解.NET)。一个非常重要的例子是Sink.ignore
,其实现为Future[Done]
。它为您提供了一个句柄,说明您实现的特定流何时将完全消耗其输入(或抛出错误)。更一般地说,具体化的价值为你提供了关于你的流中的内容的一些间接信息(对于这个陈述的模糊性感到遗憾,但是手头的原则对于我来说太笼统了以至于更加明确)。
在构建图形时,您将不同的部分放在一起,这些部分都具有不同的具体化值。由于您的可运行图只能有一个,因此需要以某种方式组合它们。 Keep.{right, left, both, none}
是简单的函数,它们通过仅保留其中一个值或两者或无值来组合这些值。但是,即使您决定不保留它们,也不会改变这两个图形将被实现并生成的事实。