webflux框架是基于Project Reactor构建的,是一种响应式的编码方式,因此webflux的源码跟其它框架看起来差别较大。代码实际执行时也是异步的,哪怕通过debug断点跟进流程,也显得有些难懂。
为了看明白webflux的代码,本文梳理了reactor的基本接口设计、代码执行逻辑。
通过逐个方法的跟进,可以发现,所谓的响应式编程并不是魔法,对于具体的方法而言,还是一个线程同步、阻塞的去执行。所谓的响应式,需要相对应的客户端配合,这个待后续文章讨论。
1. 接口概述
分析之前,先看一下reactor提供的顶级接口
发布者
Publisher
是一个可以发送无限序列元素的发布者,允许调用多次,每次调用都会启动一个新的Subscription
。每个Subscription只能被一个Subscriber使用;Subscriber消费者只能订阅一次Publisher。
//发布者
public interface Publisher<T> {
/**
* 订阅方法
* 请求发布者启动数据流
* @param s 消费者
*/
public void subscribe(Subscriber<? super T> s);
}
订阅者(消费者)
其中Subscriber#onSubscribe
只会被调用一次
public interface Subscriber<T> {
/**
* 该方法在调用Publisher#subscribe(Subscriber)后执行
* 在Subscription#request(long)调用之前不会有数据流消费
* 如果订阅者想要消费更多的数据,需要调用Subscription#request(long)来请求数据。
*/
public void onSubscribe(Subscription s);
/**
* 消费下一个消息
* 在调用Subscription#request(long)方法时,Publisher会通过这个方法来通知订阅者消息
* @param t 数据元素
*/
public void onNext(T t);
public void onError(Throwable t);
public void onComplete();
}
Subscription
public interface Subscription {
public void request(long n);
public void cancel();
}
Subscription
的生命周期是订阅者对发布者的一次消费- 一个
Subscription
只能被一个Subscriber
使用 - Subscription不仅允许请求数据,也允许取消对数据的请求,并且支持资源清理
- 在通过
org.reactivestreams.Subscription#request
发送需求信号之前,不会有任何事件产生。该方法请求的数量最大是Long.MAX_VALUE
Processor
一种即是发布者又是消费者的组件
public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
}
2. 方法执行
2.1 subscribe方法说明
示例:
Flux.just(1, 2, 3, 4, 5)
.subscribe(new CoreSubscriber<>() {//这里传入CoreSubscriber对象作为订阅者
@Override
public void onSubscribe(Subscription s) {
log.info("onSubscribe, {}", s.getClass());
s.request(5);
}
@Override
public void onNext(Integer integer) {
log.info("onNext: {}", integer);
}
@Override
public void onError(Throwable t) {
}
@Override
public void onComplete() {
log.info("onComplete");
}
});
以下是调用的方法跟踪,调用 Flux.just
方法:
查看示例中的代码可知 subscribe()
方法调用的是 FluxArray#subscribe
通过 FluxArray#subscribe
方法调用到我们定义的 onSubscribe
方法,进而调用 FluxArray.ArraySubscription#request
方法
在 FluxArray.ArraySubscription#slowPath
方法中循环拉去数组中的元素,执行onNext
方法,最后执行 onComplete
方法
[!NOTE] 总结
Publisher
中定义了数据源Subscriber
中定义了数据处理的动作Subscription
中定义了数据处理的过程,编排了 Subscriber 中定义的方法
2.2 processor 方法说明
示例:
Flux.just(1, 2, 3, 4, 5)
.map(i -> i * i)
.subscribe(new CoreSubscriber<>() {
//省略,与之前相同
});
跟进执行的方法到 Flux#map
方法:
之后调用的 subscribe
方法是 FluxMapFuseable#subscribe
此时执行 FluxArray#subscribe
传入的不再是 FluxArray.ArraySubscription
对象,变成了 FluxMapFuseable.MapFuseableSubscriber
对象了,由于中间过程的组装,之后会进一步调用至 FluxMapFuseable.MapFuseableSubscriber#onSubscrib
此处调用 onSubscribe
方法时,传入的参数为this,即位为 MapFuseableSubscriber
对象,因此执行s.request(5)
时,s 是 MapFuseableSubscriber
对象,实际执行的方法是
FluxMapFuseable.MapFuseableSubscriber#request
最终还是委托 FluxArray.ArraySubscription#request
方法执行。由于这一层封装,导致执行 onNext
方法时,执行的是 FluxMapFuseable.MapFuseableSubscriber#onNext
方法,这里充当了消费者的角色
真的很绕
3. 其它示例说明
这是一个更通用的场景,通过异步任务定义Publisher
测试示例:
public void test(){
Mono.fromCallable(() -> {
// 执行阻塞操作,例如数据库查询
return "Some blocking operation result";
})
.subscribe(result -> {
System.out.println("Result: " + result);
});
}
调用方法 Mono#fromCallable
时,返回的是 MonoCallable
对象
调用 subscribe()
方法时,调用的是 Mono#subscribe
方法,但会逐层封装(此处封装为LambdaMonoSubscriber
)
最终在此会调用到不同的实现,此处 this 即为 MonoCallable
对象
调用的subscribe最终为:MonoCallable#subscribe
actual 是 LambdaMonoSubscriber
对象,调用其 subscribe
方法
查看
LambdaMonoSubscriber#onSubscribe
继续跟进,调用MonoSubscriber#request
因此,此处actual.onSubscribe(sds)
并没有实际意义
LambdaMonoSubscriber.complete
方法执行时,调用的是 LambdaMonoSubscriber
的onNext、onComplete方法,实际执行的就是示例中定义的
System.out.println("Result: " + result)
至此,全部方法执行完毕。
参考:
[1] reactor3 源码分析