領先一步
VMware 提供培訓和認證,為您的進步加速。
瞭解更多響應式 Spring 社群的各位朋友!
團隊目前仍在全力投入 3.1 系列的開發,但我們也想讓社群有機會先睹為快地瞭解未來 3.2 系列即將推出的內容。
特別是,計劃在 3.2.0.RELEASE 版本中加入的一大亮點是我們一直稱之為“錯誤模式”、“繼續模式”,或者最近更官方的說法——“錯誤策略”。
說起來很簡單:如果在運算子中執行的使用者程式碼丟擲異常後可以從中恢復,從而允許序列繼續(*continue*)執行呢?
讓我們舉個例子,假設你有一個如下方法
public Flux<Integer> divide100By(Flux<Integer> dividers) {
return dividers.map(div -> 100 / div);
}
如果 `dividers` 源在某個時刻發出 `0`,那麼結果 `Flux` 將立即因 `ArithmeticException` 的 `onError` 訊號而終止。
如果源是 `Flux.range(0, 10)`,那麼仍然有 9 個完全有效的值可以被對映(mapped)。
你如何才能做到只忽略這種瞬時異常(且僅此異常),並讓後續有效值有機會得到處理呢?
在 Reactor 3.1 中,你可以透過使用 `flatMap` 為每個元素建立內部序列,並依次將錯誤恢復運算子應用於這些細粒度序列來應用一種變通方法。
public Flux<Integer> divide100By(Flux<Integer> dividers) {
return dividers.flatMap(div -> (1)
Mono.just(100 / div) (2)
.doOnError(e -> { (3)
if (e instanceof ArithmeticException) process(e); (4)
})
.onErrorResume(ArithmeticException.class, e -> Mono.empty()) (5)
);
}
我們不使用 `map`,而是使用 `flatMap`,為每個值生成一個小的內部 `Mono`
那個 `Mono` 本質上就是舊的 `map` 操作……
……並添加了錯誤恢復。
首先,我們確保在“恢復”之前處理(例如記錄日誌)`ArithmeticException`(且僅此異常)
然後我們使用帶有 `Mono.empty()` 的 `onErrorResume` 來有效地忽略結果序列中的異常
這確實可行,但寫起來有點麻煩(儘管 `compose` 和 `transform` 可以幫助實現這類程式碼的複用)。此外,我們從單個 `map` 運算子轉變為帶有多個內部運算子的 `flatMap`。
`flatMap` 比 `map` 有更多的開銷,因為它需要協調多個源。儘管像運算子融合(operator fusion)這樣的技術可以降低這個成本,但開銷依然存在。
如果我們想進一步降低這種處理方式的開銷,困難在於我們現在必須在每個運算子的實現層面進行工作。
鏈中的每個運算子都必須以某種方式知道異常應該被捕獲但不透過 `onError` 傳播,而是以不同的方式處理。這是一個相當大的變化,並且是橫向的(transverse)!
請注意,這在概念上聽起來很像 `filter`,但它是針對錯誤的。就像 `filter` 一樣,這意味著一個運算子在處理其源的 `onNext` 丟擲異常後繼續處理時,也應該向其源請求至少一個額外的元素。
儘管它可以被隔離到特殊的執行路徑中,但這仍然是對運算子核心實現的複雜更改。
接著是 API 的問題:將其作為建構函式引數或在 `Flux` 中新增帶有“錯誤恢復”布林值的額外過載會非常繁瑣……我們真的需要為了支援這個特性而將 `Flux` API 中的方法數量翻倍嗎?
幸運的是不必:從 3.1 版本開始,我們有了 `Context`,它是將此類資訊傳播到鏈中每個 (Reactor) 運算子的良好手段。
所以這就是我們為錯誤策略特性所採取的路徑
支援僅新增到**特定運算子**(`map`、`filter`、`flatMap`、`handle` 等)。這些運算子有一個特殊的 javadoc 標籤來記錄這一事實。
透過在給定的 `Flux` 的 `Context` 中放入一個特殊鍵來啟用該特性
每個支援的運算子在其 `onNext` 實現中都有一個特殊路徑,它檢查該鍵,如果找到,將改變其處理錯誤的方式。
該特性透過 `errorStrategyContinue()` API 向用戶公開
它可以更細粒度:可以過濾哪些異常可以恢復,並且可以為此類恢復的異常設定自定義處理器。
重要
需要記住的一點是,由於這是透過 `Context` 啟用的,該特性遵循與 `Context` 相同的傳播規則。例如,它將在 `flatMap` 的內部序列上啟用。如果這不是期望的行為,請在 flatMap 內部使用 `errorStrategyStop()` 恢復預設行為(這不會超出 flatMap 的範圍)。**它也會向後傳播,在 `errorStrategyXXX` 之前的運算子上啟用**。
以下是我們的先前示例在使用錯誤策略後的樣子
public Flux<Integer> divide100By(Flux<Integer> dividers) {
return dividers.map(div -> 100 / div) (1)
.errorStrategyContinue(ArithmeticException.class, (2)
(error, value) -> process(error)); (3)
}
回到簡單的 `map`
我們只從 `ArithmeticException` 恢復
我們將此類異常傳遞給我們內部的處理器(注意,如果存在,我們還可以訪問導致異常的原始值)
我們剛剛釋出了 `3.2.0.M1` 里程碑版本[1],主要包含錯誤策略特性,我們希望您能對其進行測試?
注意
這是一個如此橫向的變化,即使您不打算使用它,用該工件(artifact)執行您的測試也是有價值的,以驗證如果您沒有明確使用 `errorStrategyContinue()`,行為不應有任何變化(因為該特性包含在特定的執行路徑中)。
**為了獲取里程碑版本**,將 `repo.spring.io/milestone` 倉庫新增到您的 Maven 或 Gradle 構建配置中,並獲取 `reactor-core` `3.2.0.M1` 工件(artifact)
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.2.0.M1</version>
</dependency>
特別是對於該特性的 API,目前還沒有最終確定。因此,如果您有任何反饋,請透過在 GitHub 上提出 issue 或在 Gitter 上討論該特性,隨時告知我們。
在此期間,祝您編碼愉快!
1. 附註:這個里程碑版本提前釋出了,當時 3.1.3.RELEASE 仍在開發中,因此請注意它不包含 3.1.3 及後續 3.1.x 版本中的所有修復。