本系列其他文章见:《响应式Spring的道法术器》。
前情提要:响应式流 | Reactor3快速上手
2 响应式编程之法
上一章本着“快速上手”的原则,介绍了响应式流的概念,以及Reactor 3的使用。这一章,我们基于Reactor 3的实现原理,从《响应式流规范》入手,深入了解响应式流开发库。
2.1 响应式流规范
现代软件对近乎实时地处理数据的需求越来越强烈,对不断变化的信息的即时响应,意味着更大的商业价值,流处理是一种快速将数据转换为有用信息的手段。
数据流中的元素可以是一个一个的待计算的数据,也可以是一个一个待响应的事件。前者多用于大数据处理,比如Storm、Spark等产品,后者常用于响应式编程,比如Netflix在使用的RxJava、Scala编程语言的发明者Typesafe公司(已更名为Lightbend)的Akka Stream、Java开发者都熟悉的Pivotal公司的Project Reactor、走在技术前沿的Vert.x等。
软件行业是一个非常注重分享和交流的行业。随着对响应式编程技术的讨论与沟通逐渐深入,2013年末的时候,Netflix、Pivotal、Typesafe等公司的工程师们共同发起了关于制定**“响应式流规范(Reactive Stream Specification)”**的倡议和讨论,并在github上创建了reactive-streams-jvm项目。到2015年5月份,1.0版本的规范出炉,项目README就是规范正文。
各个响应式开发库都要遵循这个规范,其好处也是显而易见的。之所以我们编写的Java代码可以在Hotspot、J9和Zing等JVM运行,是因为它们都遵循Java虚拟机规范。类似的,由于各个响应式开发库都遵循响应式流规范,因此互相兼容,不同的开发库之间可以进行交互,我们甚至可以同时在项目中使用多个响应式开发库。对于Spring WebFlux来说,也可以使用RxJava作为响应式库。
虽然响应式流规范是用来约束响应式开发库的,作为使用者的我们如果能够了解这一规范对于我们理解开发库的使用也是很有帮助的,因为规范的内容都是对响应式编程思想的精髓的呈现。访问reactive-streams-jvm项目,可以浏览规范的细节,包括其中定义的响应式流的特点:
- 具有处理无限数量的元素的能力;
- 按序处理;
- 异步地传递元素;
- 必须实现非阻塞的回压(backpressure)。
2.1.1 响应式流接口
响应式流规范定义了四个接口,如下:
1.Publisher
是能够发出元素的发布者。
public interface Publisher<T> {
public void subscribe(Subscriber<? super T> s);
}
2.Subscriber
是接收元素并做出响应的订阅者。
public interface Subscriber<T> {
public void onSubscribe(Subscription s);
public void onNext(T t);
public void onError(Throwable t);
public void onComplete();
}
当执行subscribe
方法时,发布者会回调订阅者的onSubscribe
方法,这个方法中,通常订阅者会借助传入的Subscription
向发布者请求n个数据。然后发布者通过不断调用订阅者的onNext
方法向订阅者发出最多n个数据。如果数据全部发完,则会调用onComplete
告知订阅者流已经发完;如果有错误发生,则通过onError
发出错误数据,同样也会终止流。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W6SjdkUi-1615631542787)(]
订阅后的回调用表达式表示就是onSubscribe onNext* (onError | onComplete)?
,即以一个onSubscribe
开始,中间有0个或多个onNext
,最后有0个或1个onError
或onComplete
事件。
Publisher
和Subscriber
融合了迭代器模式和观察者模式。
我们经常用到的Iterable
和Iterator
就是迭代器模式的体现,可以满足上边第1和2个特点关于按需处理数据流的要求;而观察者模式基于事件的回调机制有助于满足第3个特点关于异步传递元素的要求。
3.Subscription
是Publisher
和Subscriber
的“中间人”。
public interface Subscription {
public void request(long n);
public void cancel();
}
当发布者调用subscribe
方法注册订阅者时,会通过订阅者的回调方法onSubscribe
传入Subscription
对象,之后订阅者就可以使用这个Subscription
对象的request
方法向发布者“要”数据了。回压机制正是基于此来实现的,因此第4个特点也能够实现了。
4.Processor
集Publisher
和Subscriber
于一身。
public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
}
这四个接口在JEP 266跟随Java 9版本被引入了Java SDK。
这四个接口是实现各开发库之间互相兼容的桥梁,响应式流规范也仅仅聚焦于此,而对诸如转换、合并、分组等等的操作一概未做要求,因此是一个非