响应式Spring Reactor3设计原理简析

一、响应式编程

(一)简介

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描述
Publisherorg.reactivestreams.Publisher生产者,通过此接口发布元素序列给有需求的消费者
Subscriberorg.reactivestreams.Subscriber消费者,从Publisher那里获取所需的元素进行消费
Subscriptionorg.reactivestreams.Subscription连接Subscriber和Publisher进行交流的中间人
Proccessororg.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.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值