
本文是精講回應式WebClient第6篇,前篇的blog訪問地址如下:
- 精講回應式webclient第1篇-回應式非阻塞IO與基礎用法
- 精講回應式WebClient第2篇-GET請求阻塞與非阻塞呼叫方法詳解
- 精講回應式WebClient第3篇-POST、DELETE、PUT方法使用
- 精講回應式WebClient第4篇-檔案上傳與下載
- 精講回應式WebClient第5篇-請求超時設定與例外處理
在上一篇我們為大家介紹了WebClient的例外處理方法,我們可以對指定的例外進行處理,也可以分類處理400-499、500-599狀態碼的HTTP例外,
我們本節為大家介紹的實際上是另外一種例外處理機制:請求失敗之后自動重試,當WebClient發起請求,沒有得到正常的回應結果,它就會每隔一段時間再次發送請求,可以發送n次,這個n是我們自定義的,n次請求都失敗了,最后再將例外拋出,可以通過我們上一節交給大家的方法進行例外處理,也就是針對連接超時例外、讀寫超時例外等,或者是HTTP回應結果為非正常狀態碼(不是200狀態碼段),都在自動重試機制的范疇內,
如果您覺得我的文章對您有幫助的話,請幫忙點贊或分享,您的支持是我不竭的創作動力!
一、請求例外重試
下面的代碼是請求"http://jsonplaceholder.typicode.com" 網站的服務,該網站是一個免費提供HTTP請求測驗的服務端網站,我們可以用它測驗WebClient,需要注意的是:正常的GET方法請求地址是"/posts/1",我特意的把它寫錯成為"/postss/1",這樣可以觸發404資源無法找到的例外,
public class ReTryTest {
@Test
public void testRetry() {
WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.build();
Mono<String> mono = webClient
.get() //GET 請求
.uri("/postss/1") // 請求路徑,注意為了制造例外,這里是錯的
.retrieve() //獲取請求結果
.bodyToMono(String.class) //用Mono接收單個非集合物件資料
.doOnError(Exception.class, err -> { //處理例外
System.out.println(LocalDateTime.now() + "---發生錯誤:" +err.getMessage() );
})
.retry(3);
System.out.println("=====" + mono.block());
}
}
- doOnError例外處理是我們在上一節文章中為大家介紹的例外處理函式,我們在這里列印日志,觀察重試次數
- retry(3)就是重點了,表示請求失敗之后重試3次請求,也可以使用retry()無參方法,不設定次數,可以無限重試,這樣顯然不好,我們一般不用,
下面是doOnError中列印的控制臺輸出內容,一共列印了4次,(一次失敗 + 三次重試失敗)

二、重試時間間隔設定
上面的請求重試方法,請求失敗之后立即重試,在很短的時間內就完成了3次重試,如果這是在生產環境下,可能你的服務端因為資源緊張造成請求回應超時等例外,這種重試機制無疑會讓本就不堪重負的服務端雪上加霜,我們下面交給大家一種為重試設定時間間隔的方法:
.retryBackoff(3, Duration.ofSeconds(5));
- 第一個引數仍然表示重試3次
- 第二個引數表示按指數增長的時間間隔重試,第一次重試間隔5秒,第二次間隔10秒(5 x2),第三次間隔20秒(5x2x2)
原始碼如下:

三、retryWhen方法
上面的retryBackoff方法雖然已經一定程度上緩解了請求重試導致的服務端的壓力,但是它還是不分場景的不斷重試,
- 在實際的開發中,可以請求重試的場景應該是:網路例外、請求超時例外、服務端突然面臨高并發導致的臨時處理能力不足導致的超時等這種由于外部原因導致的例外場景,
- 對于那些由于程式員撰寫的bug、資源訪問權限不足、資源找不到、HTTP版本不受支持等造成的例外,重試一萬次也不會成功,反而可能因為你不斷的重試造成服務器崩潰,
所以說Webclient已經在原始碼中,將retryBackoff()標記為廢棄,建議使用retryWhen()代替它,retryWhen()可以指定針對某些例外進行重試,其他例外不做重試,

為了使用retryWhen(),需要引入下面的包
<dependency>
<groupId>io.projectreactor.addons</groupId>
<artifactId>reactor-extra</artifactId>
</dependency>
3.1.人為制造超時例外-用于測驗
為了能夠制造請求超時的例外場景,我們給連接超時設定為5毫秒,即:讓所有請求一定會超時,(沒有任何請求能在5毫秒內完成網路連接)
//認為設定請求超時時間為5毫秒,也就是請求一定會超時,一定會拋出ConnectTimeoutException
TcpClient tcpClient = TcpClient
.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5); //5毫秒
WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)))
.build();
3.2.測驗retryWhen
用Retry物件定義請求重試的條件,也就是retryWhen的when
Retry<?> retry = Retry.onlyIf(x -> x.exception() instanceof ConnectTimeoutException)
.retryMax(3) // 重試3次
.backoff(Backoff.exponential(Duration.ofSeconds(5),Duration.ofSeconds(60),2,true));
Mono<String> mono = webClient
.get() //GET 請求
.uri("/posts/1") // 請求路徑,這里的請求路徑是正確的
.retrieve()
.bodyToMono(String.class)
.retryWhen(retry); //滿足Retry條件進行重試
System.out.println("=====" + mono.block());
Retry.onlyIf(x -> x.exception() instanceof ConnectTimeoutException)表示只有針對ConnectTimeoutException連接超市例外才進行請求重試,這里使用了java8的Predicate語法- Backoff.exponential表示按指數增長的時間間隔進行重試,可以自己指定指數重試因子,即指數的計數,這里我們仍然使用2作為指數重試因子,第一次重試間隔5秒,第二次間隔10秒(5 x2),第三次間隔20秒(5x2x2)
- 為防止間隔時間指數級無限延長,Backoff.exponential最長的重試間隔不能超過60秒,第二個引數,
- retryWhen(retry) 滿足retry條件進行重試
3.3.retryWhen的其他方法

- onlyIf()表示捕獲到指定的某個例外,進行請求重試
- allBut()表示除了某個例外之外,其他的例外被捕獲則進行請求重試
- any() 表示針對所有例外,進行請求重試
- anyOf()表示指定某些例外型別,進行請求重試

backOff表示重試的時間間隔
- exponential()指數級增長的時間間隔
- fix()表示固定的時間間隔
歡迎關注我的博客,里面有很多精品合集
- 本文轉載注明出處(必須帶連接,不能只轉文字):字母哥博客,
覺得對您有幫助的話,幫我點贊、分享!您的支持是我不竭的創作動力! ,另外,筆者最近一段時間輸出了如下的精品內容,期待您的關注,
- 《手摸手教你學Spring Boot2.0》
- 《Spring Security-JWT-OAuth2一本通》
- 《實戰前后端分離RBAC權限管理系統》
- 《實戰SpringCloud微服務從青銅到王者》
- 《VUE深入淺出系列》
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/49423.html
標籤:Java
