一、响应式编程
(一)简介
2009年微软为了应对高并发的服务器端开发,提出了Reactive Programming,中文称响应式编程(或反应式编程)。之后Java 社区如Netflix 和 TypeSafe 公司提供了 RxJava 和 Akka Stream 技术,让 Java 平台也有了能够实现反应式编程的框架,但因缺少简单易用的技术将反应式编程推广普及,并同诸如 MVC 框架、HTTP 客户端、数据库技术等整合,所以整体应用范围并不大。
下面结合响应式编程的如下几个概念逐步展开介绍响应式Spring Reactor3设计原理。
(二) 异步编程
基于异步的开发模式是相对于同步的开发模式而言。结合具体场景而言,所谓同步是指程序使用者发出请求后,无法使用其他功能而只能等待直到该请求程序处理结果返回;而异步是指程序使用者发出某个请求后,不用等待该请求的程序处理结果返回,而是可正常使用其他功能,待使用其他功能后回过头来可正常查看原有请求的程序处理结果。无论作为开发者还是程序使用者,都不希望程序处理总是处于等待、在等待的状态,通过异步编程模式,程序使用者可以在明知某个任务会耗时较长的情况下,无等待的使用其他功能,从而获得良好的使用体验。但相对于同步编程,异步编程相对较为复杂。
1、并发和并行
提到异步,不得不提并发和并行。对于操作系统而言:
并发是指同一时间操作系统可同时接收多个任务(但并不意味着同时处理这多个任务);
并行是指操作系统同一时间同时运行处理多个任务,这就要求是多核CPU,对于单核CPU而言不存在什么并行;
所以并行是并发的一种特殊形式,也就是在多线程环境下,这些线程在同一时间调度内分配到不同CPU上同时执行的情况。
异步编程的核心本质就是将一个大任务拆分为多个小任务(元素),然后充分利用多核CPU的资源,并行进行小任务(元素)执行,最后提升整体大任务的处理时效。除此之外还需要对多个小任务(元素)的执行先后以及结果进行协调和控制,这样才能保证执行结果的可用性和准确性。对于响应编程来讲,对多任务(元素)的协调控制手段就包括背压。
2、背压
所谓背压就是协调数据源生产者和消费者的缓存区间。以传统的生产者消费者模式来讲,任务(元素)由生产者生产发布,由消费者订阅接收处理,但若生产者的生产速度超过了消费者的处理速度,就会造成任务(元素)的积压,随着这种积压不断增加,程序性能就会受影响,而背压机制就是解决这种问题的。
具体来讲,任务(元素)由生产者生产发布,由消费者订阅,消费者会根据自身处理能力向生产者发送一个信号(或拉取),告知生产者它接收多少个任务(元素),以此来保证所需任务(元素)下发到消费者;所以背压应该具有承载任务(元素)的能力,且其存储和下发的任务(元素)要具有先后顺序,而队列最合适来做这个事情。
3、流
通过上面两部分介绍,异步编程的本质是对多任务(元素)的处理,现有JDK中Collections框架提供了丰富的API来处理多元素,但其并不能高效的利用多核CPU优势;不过JDK8之后的流则很好的解决了这个问题。流具有如下特性:
- 流中的元素是有序的
- 流需要一个数据源,比如集合、数组、文件等
- 丰富的API等。
(三)响应式流规范
Reactive Streams—响应式流规范,是2013年底由 Netflix、TypeSafe、Pivotal 等公司为实现 Reactive Programming 思想而发起制定的一个倡议规范,其本质就是使用流来快速处理和响应多任务(元素),实现通过异步并行处理多任务,充分利用多核CPU性能的特点,同时提供背压机制,实现对资源提供过载保护的功能。基于此规范定义了一套标准的java版响应式编程标准API,如:Publisher、Subscriber、Subscription等。
目前很多工具库均实现了Reactive Streams—响应式流规范,包括Akka(TypeSafe公司开发)、Reactor(Pivatol公司开发)、RxJava(Netflix公司开发)等,同时JDK9中也开始支持响应式编程,核心类为java.util.concurrent.Flow,其中定义了Publisher、Subscriber、Subscription、Proccessor等核心响应式编程接口。本文重点基于spring5介绍Reactor的核心逻辑。
二、Spring Reactor3中Flux初探
(一)Reactor简介
Reactor 框架是 Pivotal 公司开发的,实现了 Reactive Programming 思想,符合Reactive Streams—响应式流规范的一项技术。
上图展示了Spring、Reactor和RxJava之前的联系:
1、Spring5是基于Reactor框架实现响应式流的,其中Spring WebFlux是完全依赖reactor-core来实现;
2、Reactor与RxJava2共用了Reactor Streams Commons标准API接口,实现了API的通用。
Reactor各组件简单介绍如下:
1、reactor-core:Reactor的核心实现库;
2、reactor-ipc:针对encode、decode他、send(unicast、multicast或request/response)及服务连接而设计的支持背压的组件;支持Kafka、Netty;
3、reactor-addons:其中包括reactor-adapter(各种适配)、reactor-logback(日志异步处理支持)、reactor-extra(数学类型的Flux源头提供数学运算支持);
4、reactor-streams-commons:Reactor与RxJava2共用的一套标准API接口。
(二)Reactor核心接口
Reactor中实现了标准的Publisher、Subscriber、Subscription、Proccessor核心接口():
接口 | API | 描述 |
---|---|---|
Publisher | org.reactivestreams.Publisher | 生产者,通过此接口发布元素序列给有需求的消费者 |
Subscriber | org.reactivestreams.Subscriber | 消费者,从Publisher那里获取所需的元素进行消费 |
Subscription | org.reactivestreams.Subscription | 连接Subscriber和Publisher进行交流的中间人 |
Proccessor | org.reactivestreams.Proccessor | 既是消费者也是生产者 |
其中Publisher最常用的两个实现Mono和Flux:
1、Mono:表示一个特殊的Publisher,它可以发送0个或1个元素
2、Flux:表示一个特殊的Publisher,它可以发送0个或n个元素。
(三)Flux源码解析
1、Reactive Streams—响应式流 标准流程如下:
2、Flux流程解析
此处结合示例对Flux源码逻辑进行分析,分为两步:
- 1. 创建Flux实例,并初始化产生10 个元素,供消费者处理;
- 2. 调用subscribe方法,触发消费者处理
//1. 产生10 个元素 并进行处理
Flux.create(sink -> {
for (int i = 0; i < 10; i++) {
sink.next(i);
}
sink.complete();
})
.subscribe(System.out::println); // 2. 在调用该方法时,消费者开始请求获取元素进行消费处理
(1)创建Flux实例,并初始化产生10 个元素,供消费者处理:
// reactor.core.publisher.Flux#create(java.util.function.Consumer<? super reactor.core.publisher.FluxSink<T>>)
//1-2. 此处是以函数式表达式Consumer对象来处理,同时设置背压策略OverflowStrategy.BUFFER
public static <T> Flux<T> create(Consumer<? super FluxSink<T>> emitter) {
return create(emitter, OverflowStrategy.BUFFER);
}
// reactor.core.publisher.Flux#create(java.util.function.Consumer<? super reactor.core.publisher.FluxSink<T>>, reactor.core.publisher.FluxSink.OverflowStrategy)
//1-3. 此处创建了FluxCreate对象进行处理,该对象实现了Flux接口,同时设置背压模式
public static <T> Flux<T> create(Consumer<? super FluxSink<T>> emitter, OverflowStrategy backpressure) {
return onAssembly(new FluxCreate<>(emitter, backpressure, FluxCreate.CreateMode.PUSH_PULL));
}
// reactor.core.publisher.FluxCreate#FluxCreate
//1-4. 截止目前Flux实例创建成功,但并未触发消费者进行消费处理
FluxCreate(Consumer<? super FluxSink<T>> source,
FluxSink.OverflowStrategy backpressure,
CreateMode createMode) {
this.source = Objects.requireNonNull(source, "source");
this.backpressure = Objects.requireNonNull(backpressure, "backpressure");
this.createMode = createMode;
}
(2)调用subscribe方法,触发消费者处理
// reactor.core.publisher.Flux#subscribe(java.util.function.Consumer<? super T>)
// 2-1. 此处执行subscribe方法
public final Disposable subscribe(Consumer<? super T> consumer) {
Objects.requireNonNull(consumer, "consumer");
return subscribe(consumer, null, null);
}
// 实际执行的是reactor.core.publisher.Flux#subscribe(java.util.function.Consumer<? super T>, java.util.function.Consumer<? super java.lang.Throwable>, java.lang.Runnable, java.util.function.Consumer<? super org.reactivestreams.Subscription>)
// 2-2. 其中传入的消费者是new了一个LambdaSubscriber类实例
public final Disposable subscribe(
@Nullable Consumer<? super T> consumer,
@Nullable Consumer<? super Throwable> errorConsumer,
@Nullable Runnable completeConsumer,
@Nullable Consumer<? super Subscription> subscriptionConsumer) {
return subscribeWith(new LambdaSubscriber<>(consumer, errorConsumer,
completeConsumer,
subscriptionConsumer));
}
// 2-3. 最终会执行到reactor.core.publisher.FluxCreate#subscribe方法中
@Override
public void subscribe(CoreSubscriber<? super T> actual) {
//2-3-1. 此处首先根据背压策略包装了一个用于元素下发动作的BaseSink类,其实现了Subscription接口,实际是BufferAsyncSink,此处充当了subscription角色
BaseSink<T> sink = createSink(actual, backpressure);
//2-3-2. 此处遵循Reactive Streams—响应式流标准规范,携带subscription调用了subscriber的onSubscribe()方法
// 实际的消费者actual为2-2创建的LambdaSubscriber类示例
actual.onSubscribe(sink);
try {
source.accept(
createMode == CreateMode.PUSH_PULL ? new SerializedSink<>(sink) :
sink);
}
catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
sink.error(Operators.onOperatorError(ex, actual.currentContext()));
}
}
static <T> BaseSink<T> createSink(CoreSubscriber<? super T> t,
OverflowStrategy backpressure) {
switch (backpressure) {
case IGNORE: {
return new IgnoreSink<>(t);
}
case ERROR: {
return new ErrorAsyncSink<>(t);
}
case DROP: {
return new DropAsyncSink<>(t);
}
case LATEST: {
return new LatestAsyncSink<>(t);
}
default: {
return new BufferAsyncSink<>(t, Queues.SMALL_BUFFER_SIZE);
}
}
}
// 2-4. 此处执行实际的消费者actual的具体业务逻辑 reactor.core.publisher.LambdaSubscriber#onSubscribe
@Override
public final void onSubscribe(Subscription s) {
if (Operators.validate(subscription, s)) {
this.subscription = s;
if (subscriptionConsumer != null) {
try {
//2-4-1. 有元素就处理,消费者实际的业务处理
subscriptionConsumer.accept(s);
}
catch (Throwable t) {
Exceptions.throwIfFatal(t);
s.cancel();
onError(t);
}
}
else {
//2-4-2. 没有元素,则请求拉取新的元素,s是2-3中的BaseLink实例,实际是BufferAsyncSink
s.request(Long.MAX_VALUE);
}
}
}
// 2-5. 此处执行reactor.core.publisher.FluxCreate.BaseSink#request
@Override
public final void request(long n) {
if (Operators.validate(n)) {
Operators.addCap(REQUESTED, this, n);
LongConsumer consumer = requestConsumer;
if (n > 0 && consumer != null && !isCancelled()) {
consumer.accept(n);
}
// 拉取新的元素
onRequestedFromDownstream();
}
}
// 2-6. 此处执行reactor.core.publisher.FluxCreate.BufferAsyncSink#onRequestedFromDownstream
@Override
void onRequestedFromDownstream() {
drain();
}
// 2-7. reactor.core.publisher.FluxCreate.BufferAsyncSink#onRequestedFromDownstream
void drain() {
if (WIP.getAndIncrement(this) != 0) {
return;
}
int missed = 1;
final Subscriber<? super T> a = actual;
final Queue<T> q = queue;
// 2-7-1. 此处一直监听背压缓存区中的元素,有新的元素,就执行消费者 actual.onNext()逻辑进行具体业务逻辑处理
for (; ; ) {
long r = requested;
long e = 0L;
while (e != r) {
if (isCancelled()) {
Operators.onDiscardQueueWithClear(q, ctx, null);
return;
}
boolean d = done;
T o = q.poll();
……
// 2-7-2. 此处调用实际的消费者 actual(为2-2创建的LambdaSubscriber类示例)的onNext方法进行下一个处理逻辑
a.onNext(o);
e++;
}
if (e == r) {
if (isCancelled()) {
Operators.onDiscardQueueWithClear(q, ctx, null);
return;
}
boolean d = done;
boolean empty = q.isEmpty();
if (d && empty) {
Throwable ex = error;
if (ex != null) {
// 2-7-3. 处理失败,实际执行actual.onError(e);
super.error(ex);
}
else {
// 2-7-4. 全部完成,实际执行actual.onComplete();
super.complete();
}
return;
}
}
……
}
}
参考资料:
1、https://projectreactor.io/2.x/reference/#reactor-core
2、https://projectreactor.io/docs/core/release/reference/index.html#flux
3、http://www.reactive-streams.org/
4、知秋.Java编程方法论响应式Spring Reactor3设计与实现[M].北京:电业工业出版社,2020:15-31.