一、開篇
本文將解析 Spring 的 Reactor 項目的源碼。主要目的是讓自己能深入理解 Reactor 這個項目,以及 Spring 5 和 Spring Boot 2。
Project Reactor 項目地址:https://github.com/reactor
Reactor 項目主要包含 Reactor Core 和 Reactor Netty 兩部分。Reactor Core 實現了反應式編程的核心功能,Reactor Netty 則是 Spring WebFlux 等技術的基礎。本文將介紹 Core 模塊的核心原理。
本文從一個例子開始:
public static void main(String[] args) {
Flux.just("tom", "jack", "allen")
.filter(s -> s.length() > 3)
.map(s -> s.concat("@qq.com"))
.subscribe(System.out::println);
}
整個 Project Reactor 的核心調用分爲下面幾個階段:
- 聲明階段
- subscribe 階段
- onSubscribe 階段
- request 階段
- 調用階段
接下來對每個階段詳細介紹。
二、聲明階段
簡介
之前的文章介紹過,反應式編程和傳統風格的編程一個最大的不同點在於,開發人員編寫的大部分代碼都是在聲明處理過程。即這些代碼並不會被實際的執行,直到開始被訂閱。這便是爲何第一階段是聲明階段的原因。
詳解
先來看第一個方法 Flux.just
的源碼:
public static <T> Flux<T> just(T... data) {
return fromArray(data);
}
public static <T> Flux<T> fromArray(T[] array) {
// 省略不重要的部分
return onAssembly(new FluxArray<>(array));
}
just
方法只是創建反應式流的衆多方式的一個。在實際工作中,更常見的通過反應式 Repository 將數據庫查詢結果,或通過 Spring 5 的WebClient
將 HTTP 調用結果最爲流的開始。
onAssembly
是一個擴展機制,因爲實例中並沒有使用,所以可簡單理解爲將入參直接返回。
接下來 new FluxArray<>(array)
做的事情也很簡單,僅僅是把入參賦給成員變量。
再接下來 filter(s -> s.length() > 3)
做的事情是把上一個 Flux
,即 FluxArray
,以及 s -> s.length() > 3
所表示的 Predicate
保存起來。
看一下源碼
public final Flux<T> filter(Predicate<? super T> p) {
if (this instanceof Fuseable) {
return onAssembly(new FluxFilterFuseable<>(this, p));
}
return onAssembly(new FluxFilter<>(this, p));
}
FluxFilterFuseable(Flux<? extends T> source, Predicate<? super T> predicate) {
super(source);
this.predicate = Objects.requireNonNull(predicate, "predicate");
}
這裏有個邏輯判斷,就是返回的值分爲了 Fuseable
和非 Fuseable
兩種。簡單介紹一下,Fuseable 的意思就是可融合的。我理解就是 Flux
表示的是一個類似集合的概念,有一些集合類型可以將多個元素融合在一起,打包處理,而不必每個元素都一步步的處理,從而提高了效率。因爲 FluxArray
中的數據一開始就都準備好了,因此可以打包處理,因此就是 Fuseable
。
而接下來的 map
操作也對應一個 FluxMapFusable
。原理與 filter
操作,這裏不再重複。
三、subscribe 階段
簡介
subscribe 階段同行會觸發數據發送。在本例中,後面可以看到,對於 FluxArray
,數據發送很簡單,就是循環發送。而對於像數據庫、RPC 這樣的長久,則會觸發請求的發送。
詳解
當調用 subscribe(System.out::println)
時,整個執行過程便進入 subscribe 階段。經過一系列的調用之後,subscribe 動作會代理給具體的 Flux
來實現。就本例來說,會代理給 FluxMapFuseable
,方法實現如下(經過簡化):
public void subscribe(CoreSubscriber<? super R> actual) {
source.subscribe(new MapFuseableSubscriber<>(actual, mapper));
}
其中的 source
變量對應的是當前 Flux
的上一個。本例中,FluxMapFuseable
上一個是 FluxFilterFuseable
。
new MapFuseableSubscriber<>(actual, mapper)
則是將訂閱了 FluxMapFuseable
的 Subscriber 和映射器封裝在一起,組成一個新的 Subscriber。然後再訂閱 source
,即 FluxArray
。source
是在上一個階段被保存下來的。
這裏強調一下
Publisher
接口中的subscribe
方法語義上有些奇特,它表示的不是訂閱關係,而是被訂閱關係。即aPublisher.subscribe(aSubscriber)
表示的是aPublisher
被aSubscriber
訂閱。
接下來調用的就是 FluxFilterFuseable
中的 subscribe
方法,類似於 FluxMapFuseable
,源碼如下:
public void subscribe(CoreSubscriber<? super T> actual) {
source.subscribe(new FilterFuseableSubscriber<>(actual, predicate));
}
這時 source
就成了 FluxArray
。於是,接下來是調用 FluxArray
的 subscribe
方法。
public static <T> void subscribe(CoreSubscriber<? super T> s, T[] array) {
if (array.length == 0) {
Operators.complete(s);
return;
}
if (s instanceof ConditionalSubscriber) {
s.onSubscribe(new ArrayConditionalSubscription<>((ConditionalSubscriber<? super T>) s, array));
}
else {
s.onSubscribe(new ArraySubscription<>(s, array));
}
}
最重要的部分還是 s.onSubscribe(new ArraySubscription<>(s, array))
。不同於 FluxMapFuseable
和 FluxFilterFuseable
,FluxArray
沒有再調用 subscribe
方法,因爲它是數據源頭。而 FluxMapFuseable
和 FluxFilterFuseable
則是中間過程。
可以這樣簡單理解,對於中間過程的 Mono/Flux
,subscribe 階段是訂閱上一個 Mono/Flux
;而對於源 Mono/Flux
,則是要執行 Subscriber.onSubscribe(Subscription s)
方法。
四、onSubscribe 階段
簡介
在調用 FluxArray
的 subscribe
方法之後,執行過程便進入了 onSubscribe 階段。onSubscribe 階段指的是 Subscriber#onSubscribe
方法被依次調用的階段。這個階段會讓各 Subscriber
知道 subscribe
方法已被觸發,真正的處理流程馬上就要開始。所以這一階段的工作相對簡單。
詳解
s.onSubscribe(new ArraySubscription<>(s, array));
s 是 FilterFuseableSubscriber
,看一下 FilterFuseableSubscriber
的 onSubscribe(Subscription s)
源碼:
public void onSubscribe(Subscription s) {
if (Operators.validate(this.s, s)) {
this.s = (QueueSubscription<T>) s;
actual.onSubscribe(this);
}
}
actual 對應 MapFuseableSubscriber
。MapFuseableSubscriber
的 onSubscribe
方法也是這樣,但 actual 對於的則是代表 System.out::println
的 LambdaSubscriber
。
調用過程:
FluxArray.subscribe -> FilterFuseableSubscriber.onSubscribe -> MapFuseableSubscriber.onSubscribe -> LambdaSubscriber.onSubscribe
五、request 階段
含義
onSubscribe 階段是表示訂閱動作的方式,讓各 Subscriber
知悉,準備開始處理數據。當最終的 Subscriber
做好處理數據的準備之後,它便會調用 Subscription
的 request
方法請求數據。
詳解
下面是 LambdaSubscriber
onSubscribe
方法的源碼:
public final void onSubscribe(Subscription s) {
if (Operators.validate(subscription, s)) {
this.subscription = s;
if (subscriptionConsumer != null) {
try {
subscriptionConsumer.accept(s);
}
catch (Throwable t) {
Exceptions.throwIfFatal(t);
s.cancel();
onError(t);
}
}
else {
s.request(Long.MAX_VALUE);
}
}
}
由上可見 request
方法被調用。在本例中,這裏的 s
是 MapFuseableSubscriber
。
這裏需要說明,如 MapFuseableSubscriber
、FilterFuseableSubscriber
,它們都有兩個角色。一個角色是 Subscriber
,另一個角色是 Subscription
。因爲它們都位於調用鏈的中間,本身並不產生數據,也不需要對數據暫存,但是需要對數據做各式處理。因此,在 onSubscribe、request 階段,以及後面要講到的調用階段都需要起到代理的作用。這就解釋了 actual.onSubscribe(this)
onSubscribe 自己的原因。
下面是 FilterFuseableSubscriber
的 request
方法的源碼。充分可見其代理的角色。
public void request(long n) {
s.request(n);
}
最後 ArraySubscription
的 request
方法將被調用。
與 map、filter 等操作不同,flatMap 有比較大的差異,感興趣的同學可以自己研究一下。本文先不詳細介紹。
六、調用階段
含義解釋
這一階段將會通過調用 Subscriber
的 onNext
方法,從而進行真正的反應式的數據處理。
流程分解
在 ArraySubscription
的 request
方法被調用之後,執行流程便開始了最後的調用階段。
public void request(long n) {
if (Operators.validate(n)) {
if (Operators.addCap(REQUESTED, this, n) == 0) {
if (n == Long.MAX_VALUE) {
fastPath();
}
else {
slowPath(n);
}
}
}
}
void fastPath() {
final T[] a = array;
final int len = a.length;
final Subscriber<? super T> s = actual;
for (int i = index; i != len; i++) {
if (cancelled) {
return;
}
T t = a[i];
if (t == null) {
s.onError(new NullPointerException("The " + i + "th array element was null"));
return;
}
s.onNext(t);
}
if (cancelled) {
return;
}
s.onComplete();
}
ArraySubscription
會循環數據中的所有元素,然後調用 Subscriber
的 onNext
方法,將元素交由 Subscriber 鏈處理。
於是接下來由 FilterFuseableSubscriber
處理。下面是其 onNext
方法(做了一些簡化)
public void onNext(T t) {
boolean b;
try {
b = predicate.test(t);
}
catch (Throwable e) {
Throwable e_ = Operators.onNextError(t, e, this.ctx, s);
if (e_ != null) {
onError(e_);
}
else {
s.request(1);
}
Operators.onDiscard(t, this.ctx);
return;
}
if (b) {
actual.onNext(t);
}
else {
s.request(1);
Operators.onDiscard(t, this.ctx);
}
}
其最要部分是通過 predicate
進行判斷,如果滿足條件,則交由下一個 Subscriber
處理。
下一個要處理的是 MapFuseableSubscriber
,原理類似,不再重複。
最終要處理數據的是 LambdaSubscriber
,下面是它的 onNext
方法源碼:
public final void onNext(T x) {
try {
if (consumer != null) {
consumer.accept(x);
}
}
catch (Throwable t) {
Exceptions.throwIfFatal(t);
this.subscription.cancel();
onError(t);
}
}
consumer.accept(x)
會使數據打印在命令行中。
整個流程的解釋到這裏基本結束。
七、總結
接下來我們用一個圖來回顧一下整體的流程。
參考
需要感謝下面這篇文章,幫助我很好理解了 Project Reactor 的核心處理流程。
由表及裏學 ProjectReactor:http://blog.yannxia.top/2018/06/26/java/spring/projectreactor/