分析传统开发模式和响应式编程实现方法之间的差别引出了数据流的概念
1 引言
从“流”的概念出发,并引入响应式流程规范,从而分析响应式编程中所包含的各个核心组件。
2 流的概念
由生产者生产,并由一或多个消费者消费的元素序列。这种生产者/消费者模型也称发布/订阅模型。
Reactive Streams 是 2013 年底由 Netflix、Lightbend 和 Pivotal 的工程师发起的一项计划,旨在为无阻塞异步流处理提供一个标准。解决处理元素流的问题——如何将元素流从发布者传递到订阅者,而不需要发布者阻塞,或订阅者有无限制的缓冲区或丢弃。
3 流的处理模型
拉模式
消费者主动从生产者拉取元素。
推模式
生产者将元素推送给消费者
4 流量控制
4.1 v(生产者生产数据) < v(消费者消费数据)
消费者消费数据没有任何压力,也就不需要进行流量的控制。
4.2 v(生产者生产数据) > v(消费者消费数据)
消费者可能因为无法处理过多的数据而发生崩溃。
一般就是在生产者与消费者之间加一个队列做缓冲。我们知道队列具有存储与转发的功能,所以可以用它来进行一定的流量控制。
5 队列选型
无界队列
- 优势:确保消费者消费到所有的数据
- 劣势:系统的回弹性降低,任何一个系统不可能拥有无限的资源,一旦内存等资源耗尽,系统就可能会有崩溃的风险。
有界丢弃队列
有界丢弃队列考虑了资源的限制,适用于允许丢消息的业务场景。但消息重要性很高的场景显然不可能采取这种队列。
有界阻塞队列
6 背压(Backpressure)机制
纯“推”模式下的数据流量会有很多不可控制的因素,需要在“推”模式和“拉”模式之间考虑一定的平衡性,从而优雅地实现流量控制
下游能够向上游反馈流量请求的机制。
如果消费者消费数据的速度赶不上生产者生产数据的速度,它就会持续消耗系统的资源。使得消费者可以根据自身当前的处理能力通知生产者来调整生产数据的速度,这就是背压。
7 响应式流规范
针对流量控制的解决方案以及背压机制都包含在响应式流规范中,其中包含了响应式编程的各个核心组件。
8 响应式流的核心接口
8.1 Publisher
一种可以生产无限数据的发布者。
public interface Publisher<T> { /** * 请求 Publisher 开始流式传输数据。 * 这是一个“工厂方法”,可以多次调用,每次开始一个新的 Subscription. * 每个 Subscription 只能为一个 Subscriber. * A Subscriber 只能订阅一次 Publisher * 如果 拒绝 Publisher 订阅尝试或失败,它将通过 发出 Subscriber.onError(Throwable)错误信号 */ public void subscribe(Subscriber<? super T> s); }
Publisher 里的 subscribe 方法传入 Subscriber 接口,这里用的是回调,Publisher 根据收到的请求向当前订阅者 Subscriber 发送元素。
,发出信号的元素类型。
8.2 Subscriber
可以从发布者那里订阅并接收元素的订阅者。
public interface Subscriber<T> { public void onSubscribe(Subscription s); public void onNext(T t); public void onError(Throwable t); public void onComplete(); }
Subscriber 接口定义的这组方法构成了数据流请求和处理的基本流程,其中,onSubscribe() 从命名上看就是一个回调方法,当发布者的 subscribe() 方法被调用时就会触发这个回调。而在该方法中有一个参数 Subscription,可以把这个 Subscription 看作是一种用于订阅的上下文对象。Subscription 对象中包含了这次回调中订阅者想要向发布者请求的数据个数。
当订阅关系已经建立,那么发布者就可以调用订阅者的 onNext() 方法向订阅者发送一个数据。这个过程是持续不断的,直到所发送的数据已经达到 Subscription 对象中所请求的数据个数。这时候 onComplete() 方法就会被触发,代表这个数据流已经全部发送结束。而一旦在这个过程中出现了异常,那么就会触发 onError() 方法,我们可以通过这个方法捕获到具体的异常信息进行处理,而数据流也就自动终止了。
8.3 Subscription
确保生产者、消费者针对数据处理速度达成一种动态平衡的基础,流量控制中实现背压的关键。
public interface Subscription { public void request(long n); public void cancel(); }
响应式流规范非常灵活,还可提供独立的“推”模型和“拉”模型。
响应式流是一种规范,而该规范的核心价值,就在于为业界提供了一种非阻塞式背压的异步流处理标准。
业界主流响应式开发库包括:
- RxJava
- Akka
- Vert.X
- Project Reactor
总结
本文分析了数据流的概念的分类以及“推”流模式下的流量控制问题,从而引出了响应式系统中的背压机制。
响应式流规范是对响应式编程思想精髓的呈现
对于开发人员而言,理解这一规范有助于更好的掌握开发库的使用方法和基本原理。
FAQ
简要描述响应式流规范中数据的生产者和消费者之间的交互关系。
响应式流规范中,数据的生产者和消费者之间的交互关系是基于观察者模式实现的。生产者通过创建一个可观察的数据流并向消费者提供订阅方法,消费者可以通过订阅这个数据流来获取数据。一旦生产者有新的数据产生,它会将数据发送给所有已订阅该数据流的消费者。消费者可以通过取消订阅方法来停止接收数据。这种交互关系使得生产者和消费者之间解耦,同时也允许消费者按需获取数据,从而实现了高效的异步编程。