在项目反应堆或akka流中,接收器和用户之间的概念差异是什么?

时间:2018-01-09 06:27:02

标签: akka-stream project-reactor reactive-streams

接收器和用户的概念看起来与我类似。此外,我没有看到在反应流规范中明确定义接收器的概念。

1 个答案:

答案 0 :(得分:71)

我看到Oleh Dokuka,来自Project Reactor(在那里遗漏了免责声明),posted an answer已经,但是关于Akka StreamsReactive Streams的大部分假设都不正确,所以允许我在下面澄清。

免责声明:我从早期开始就参与了Reactive Streams,并且authored大多数都是Technology Compatibility Kit。我还维护Akka和Akka Streams。

另请注意:Reactive Streams已包含在Java 9中,并且已知为java.util.concurrent.Flow.*,因此以下关于RS的所有注释与j.u.c.Flow.Subscriber和其他类型的注释完全相同。< / p>

答案

Reactive Streams是服务提供者接口(SPI)规范

Reactive Streams,特别是发布者/订阅者/订阅者/处理者类型,是Service Provider Interface。即使在关于2014年的规范earliest discussions中也证实了这一点。

在规范的早期,甚​​至规范的类型也试图隐藏Publisher,Subscriber和其他类型。遗憾的是,无论在后面如何考虑API,thus the API(!) was removed and the SPI types are all that remained

,类型都会泄漏

现在你看到Reactive Streams的一些实现声称他们直接扩展这些类型是出于某种原因的好处。这是不正确的,因为这不是,并且不是Reactive Streams接口的目标。这是对这些类型的误解 - 严格来说,Reactive Streams库同意理解的操作间接口和#34;说&#34; (协议)。

作为参考,RxJava 2.0和Reactor可以直接扩展这些类型,而Akka Streams通过将它们隐藏为应用程序开发人员编程接口仍然忠于RS的设计和原则 - 这就是为什么Sink不会扩展订阅者。这与本地支持&#34;无关。我如何看待人们声称直接的IS-A关系(相反,声称一个工作间库是你的&#34;本地&#34;是对这个概念的误解)。

Sinks and Subscribers,Sources and Publishers

  

接收器和用户的概念看起来与我类似。

正确,它们在目的和设计上是相似的。

作为Sink是有效产生订阅者的lifted representation。为简化起见,您可以将其视为&#34;订购者工厂&#34; (更具体地说,Sink是&#34;蓝图&#34;,并且Materializer采用接收器的蓝图并创建适当的RS阶段,包括用于源的订阅者和用于接收器的订阅者。 因此,当您说Sink.ignore时,它实际上是一个工厂,它将最终创建一个订阅服务器来执行所有请求和忽略,如Reactive Streams所述。与Sink上声明的所有其他方法相同。

这同样适用于Source,它将1:1与反应流Publisher联系起来。因此,Source.single(1)会在内部实现为Publisher的工作 - 只要它下游允许这样做,就会发出1个元素

A.K.A。为什么反应流中没有接收器?

如上所述,Akka的Sink并未直接扩展订阅者。然而,它基本上是他们的工厂。

您可能会问:&#34;用户在正常使用期间是否完全看不到这些发布者/订阅者类型?&#34;答案是:确实如此,这是一个功能以及设计目标(根据Reactive Streams的内容)。如果底层的Publisher和Subscriber实例一直直接暴露给用户,那么可能会错误地调用它们导致错误和混淆。如果除非明确要求,这些类型永远不会暴露,意外错误的可能性就会降低!

有些人误解了这种设计,并声称没有&#34; native&#34;在Akka Streams中支持它(这不是真的)。让我们看看在API中与订阅者分离的内容让我们获益:

  

另外,我没有看到在反应流规范中明确定义接收器的概念。

事实上,Sinks不属于Reactive Streams,这绝对没问题。

避免&#34; Sink IS-A订户&#34;

的好处

Sink是Akka Streams的一部分,其目的是提供流畅的DSL,以及Subscribers的工厂。换句话说,如果订阅者是乐高积木,Sink就是构建它们的原因(而Akka Stream Materializer就是将各种乐高积木放在一起以便运行&#34;它们)。

事实上,对于用户而言,Sink不会像其他库那样带有订阅者(sic!)的任何明确的IS-A,这对用户有益:

这是因为org.reactivestreams.Subscriber现已包含在Java 9中,并且已成为Java本身的一部分,因此库应该迁移到使用java.util.concurrent.Flow.Subscriber而不是org.reactivestreams.Subscriber。选择公开并直接扩展Reactive Streams类型的库现在将有更艰难的时间来适应JDK9类型 - 所有扩展订阅者和朋友的类都需要被复制或更改以扩展完全相同的接口,但是从不同的包。在Akka中,我们只是在被要求时公开新类型 - 从JDK9发布之日起就支持JDK9类型。

Reactive Streams是一个SPI - 一个服务提供者接口 - 它旨在让图书馆共享,以便他们可以“谈论相同的类型和协议”#34;。 Akka Streams所做的所有通信以及其他Reactive Streams库都遵守这些规则,如果您想将其他库连接到Akka Streams,您就可以做到这一点 - 给Akka Streams提供互操作类型,即订阅者,处理者或发布者;不是水槽,因为那是Akka&#34; Akka特定的&#34; DSL(特定于域的语言),它增加了便利性和其他细节,隐藏(故意!)订阅者类型。

另一个原因Akka(并且说实话其他RS实现也被鼓励这样做,但选择不这样做)隐藏这些类型是因为他们很容易做错事。 如果您传出订阅者,任何人都可以在其上调用内容,甚至可以与任何与该类型进行交互的人发送un-knowingly break rules and guarantees that the Reactive Streams Specification requires

为了避免错误发生,Akka Streams中的Reactive Streams类型被隐藏&#34;并且只有在明确要求时才会暴露 - 通过在不遵循协议的情况下意外调用“原始”反应流类型的方法来最小化人们犯错的风险。