原文
https://foojay.io/today/backp...
一月中,我基於我的文章遷移到Reactive的必要條件Spring Boot應用做了一個分享
https://www.youtube.com/watch...
因為那是一個Kotlin的聚會,我是用Kotlin程式碼展示的,同時我加了一個將程式碼庫遷移到協程的步驟。
在QA環節,有人問到是否協程實現了反壓。我承認我也不確定,所以我做了一點研究。
本文提供了關於反壓的概要資訊,還有如何用Rxjava(v3),Project Reactor和Kotlin的協程Coroutines如何處理。
什麼是反壓?
反壓是指對管道中流體的抵禦或反向作用力,導致喪失摩擦力和壓力降低。反壓的說法不太恰當,壓力是個標量,有大小,但沒有方向 -- 維基百科
在軟體中,反壓跟這有點關係但也有不同的含義:假設有一個很快的資料傳送方和一個比較慢的資料接收方,反壓是指一種機制可以反向推動傳送方不要把接收方壓垮。
無論是reactivestreams.org或java.until.concurrent.Flow,反應流都提供以下四個構建塊
- Publisher傳送元素
- Subscriber對收到的元素產生反應
- 一個Subscription來繫結Publisher和Subscriber
- 一個Processor
這是類圖:
Subscription的request()方法是反壓的頂層。
規範很直白:
Subscriber必須通過Subscription.request(long n)來傳送需求訊號後接收onNext訊號。這裡隱含的規則就是由Subscriber決定什麼時候和有多少元素需要被接收。為了避免可重入Subscription方法引起的訊號重排序,強烈推薦Subscriber方法的實現在呼叫Subscription方法的最後對任何訊號處理都是用同步的方式。推薦Subscriber請求它們可以處理的上限,因為一次只請求一個元素會導致低效的“停止和等待”協議。 -- JVM的Reactive流規範
響應流的規範很標準。它們也有基於Java的TCK。
但要定義如何管理producer傳送下游無法處理的元素就超出這個規範的範圍了。問題比較簡單,解決方法也多。每種Reactive框架都有提供方案,我們來看下。
RxJava 3的反壓
RxJava v3提供以下基礎類:
類 | 描述 |
---|---|
Flowable | 0到N號元素的流。支援Reactive-流和反壓 |
Observable | 0到N元素的流。不支援反壓 |
Single | 一個精確的流: 1個元素或一個錯誤 |
Maybe | 一個包括以下的流: 沒有元素 一個元素 或一個錯誤 |
Completable | 一個流沒有元素但: 是一個completion結束或一個錯誤的訊號 |
在這些類中,Flowable是唯一實現了Reactive流-反壓的流。因此,提供反壓不是唯一的問題。RxJava wiki指出:
反壓並沒有解決Observable過度生成或Subscriber過度消費。它只是將這個問題從處理的鏈條中移動到了一個比較好處理的地方。 --響應式進行反壓不是萬金油。
為了解決這個,RxJava提供處理“過度生產“元素的兩個主要策略:
- 將元素儲存到一個快取裡,如果沒有足夠的快取,可能會產生OutOfMemoryError。
- 丟掉資料
下圖描述了這些策略的不同實現方法:
記住onBackPressureLatest操作同使用onBackpressureBuffer(1)類似:
這張圖來自RxJava的Wiki。
與其他框架不同的是,RxJava提供方法來在傳送完所有元素後傳送溢位異常訊號。這讓消費者可以收到資料而同時清楚傳送方已經丟了資料。
Project Reactor中的反壓
Project Reactor中提供的策略與RxJava類似。
API有點不一樣。比如,如果生產者溢位Project Reactor提供一個方便的方法來拋異常:
var stream = Stream.generate(Math::random);
// RxJava
Flowable.fromStream(stream) // 1
.onBackpressureBuffer(0); // 2// Project Reactor
Flux.fromStream(stream) // 1
.onBackpressureError(); // 2
- 建立Reactive流
- 如果生產者溢位拋異常
下面是高亮了反壓能力的Flux類圖:
與其他框架相比,Project Reactor提供設定快取TTL的方法來防止溢位。
協程中的反壓
協程提供同樣的快取和失效能力。協程的基礎類是Flow。
你可以這樣使用:
flow { // 1
while (true) emit(Math.random()) // 2
}.buffer(10)
- 建一個Flow類,由下面定義content
- 定義Flow的內容
- 設定快取容量為10
結論
RxJava,Project Reactor,Kotlin協程都提供反壓能力。在生產者比消費者更快時提供兩種策略:快取資料或拋棄資料。
更多:
Reactive Streams JVM specifications
https://github.com/reactive-s...
How (not) to use Reactive Streams in Java 9+
https://blog.softwaremill.com...
RxJava Backpressure
https://github.com/ReactiveX/...
本文來自祝坤榮(時序)的微信公眾號「麥芽麵包」,公眾號id「darkjune\_think」
開發者/科幻愛好者/硬核主機玩家/業餘翻譯
轉載請註明。
B站: https://space.bilibili.com/23...
交流Email: zhukunrong@yeah.net