本文是精講響應式WebClient第6篇皮壁,前篇的blog訪問地址如下:
- 精講響應式webclient第1篇-響應式非阻塞IO與基礎(chǔ)用法
- 精講響應式WebClient第2篇-GET請求阻塞與非阻塞調(diào)用方法詳解
- 精講響應式WebClient第3篇-POST笼沥、DELETE、PUT方法使用
- 精講響應式WebClient第4篇-文件上傳與下載
- 精講響應式WebClient第5篇-請求超時設(shè)置與異常處理
在上一篇我們?yōu)榇蠹医榻B了WebClient的異常處理方法窜骄,我們可以對指定的異常進行處理锦募,也可以分類處理400-499、500-599狀態(tài)碼的HTTP異常邻遏。
我們本節(jié)為大家介紹的實際上是另外一種異常處理機制:請求失敗之后自動重試糠亩。當WebClient發(fā)起請求虐骑,沒有得到正常的響應結(jié)果,它就會每隔一段時間再次發(fā)送請求赎线,可以發(fā)送n次廷没,這個n是我們自定義的。n次請求都失敗了垂寥,最后再將異常拋出颠黎,可以通過我們上一節(jié)交給大家的方法進行異常處理。也就是針對連接超時異常滞项、讀寫超時異常等狭归,或者是HTTP響應結(jié)果為非正常狀態(tài)碼(不是200狀態(tài)碼段),都在自動重試機制的范疇內(nèi)文判。
如果您覺得我的文章對您有幫助的話过椎,請幫忙點贊或分享,您的支持是我不竭的創(chuàng)作動力戏仓!
一疚宇、請求異常重試
下面的代碼是請求"http://jsonplaceholder.typicode.com" 網(wǎng)站的服務,該網(wǎng)站是一個免費提供HTTP請求測試的服務端網(wǎng)站柜去,我們可以用它測試WebClient灰嫉。需要注意的是:正常的GET方法請求地址是"/posts/1",我特意的把它寫錯成為"/postss/1"嗓奢,這樣可以觸發(fā)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() //獲取請求結(jié)果
.bodyToMono(String.class) //用Mono接收單個非集合對象數(shù)據(jù)
.doOnError(Exception.class, err -> { //處理異常
System.out.println(LocalDateTime.now() + "---發(fā)生錯誤:" +err.getMessage() );
})
.retry(3);
System.out.println("=====" + mono.block());
}
}
- doOnError異常處理是我們在上一節(jié)文章中為大家介紹的異常處理函數(shù)股耽,我們在這里打印日志,觀察重試次數(shù)
- retry(3)就是重點了钳幅,表示請求失敗之后重試3次請求物蝙。也可以使用retry()無參方法,不設(shè)置次數(shù)敢艰,可以無限重試诬乞。這樣顯然不好,我們一般不用钠导。
下面是doOnError中打印的控制臺輸出內(nèi)容震嫉,一共打印了4次。(一次失敗 + 三次重試失斈凳簟)
二票堵、重試時間間隔設(shè)置
上面的請求重試方法,請求失敗之后立即重試逮栅,在很短的時間內(nèi)就完成了3次重試悴势。如果這是在生產(chǎn)環(huán)境下窗宇,可能你的服務端因為資源緊張造成請求響應超時等異常,這種重試機制無疑會讓本就不堪重負的服務端雪上加霜特纤。我們下面交給大家一種為重試設(shè)置時間間隔的方法:
.retryBackoff(3, Duration.ofSeconds(5));
- 第一個參數(shù)仍然表示重試3次
- 第二個參數(shù)表示按指數(shù)增長的時間間隔重試军俊,第一次重試間隔5秒,第二次間隔10秒(5 x2)捧存,第三次間隔20秒(5x2x2)
源碼如下:
三粪躬、retryWhen方法
上面的retryBackoff方法雖然已經(jīng)一定程度上緩解了請求重試導致的服務端的壓力,但是它還是不分場景的不斷重試矗蕊。
- 在實際的開發(fā)中短蜕,可以請求重試的場景應該是:網(wǎng)絡異常、請求超時異常傻咖、服務端突然面臨高并發(fā)導致的臨時處理能力不足導致的超時等這種由于外部原因?qū)е碌漠惓鼍啊?/li>
- 對于那些由于程序員編寫的bug朋魔、資源訪問權(quán)限不足、資源找不到卿操、HTTP版本不受支持等造成的異常警检,重試一萬次也不會成功,反而可能因為你不斷的重試造成服務器崩潰害淤。
所以說Webclient已經(jīng)在源碼中扇雕,將retryBackoff()標記為廢棄,建議使用retryWhen()代替它窥摄。retryWhen()可以指定針對某些異常進行重試镶奉,其他異常不做重試。
為了使用retryWhen(),需要引入下面的包
<dependency>
<groupId>io.projectreactor.addons</groupId>
<artifactId>reactor-extra</artifactId>
</dependency>
3.1.人為制造超時異常-用于測試
為了能夠制造請求超時的異常場景崭放,我們給連接超時設(shè)置為5毫秒哨苛,即:讓所有請求一定會超時。(沒有任何請求能在5毫秒內(nèi)完成網(wǎng)絡連接)
//認為設(shè)置請求超時時間為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表示按指數(shù)增長的時間間隔進行重試亿蒸,可以自己指定指數(shù)重試因子,即指數(shù)的計數(shù)掌桩。這里我們?nèi)匀皇褂?作為指數(shù)重試因子边锁,第一次重試間隔5秒,第二次間隔10秒(5 x2)拘鞋,第三次間隔20秒(5x2x2)
- 為防止間隔時間指數(shù)級無限延長砚蓬,Backoff.exponential最長的重試間隔不能超過60秒,第二個參數(shù)盆色。
- retryWhen(retry) 滿足retry條件進行重試
3.3.retryWhen的其他方法
- onlyIf()表示捕獲到指定的某個異常灰蛙,進行請求重試
- allBut()表示除了某個異常之外祟剔,其他的異常被捕獲則進行請求重試
- any() 表示針對所有異常,進行請求重試
- anyOf()表示指定某些異常類型摩梧,進行請求重試
backOff表示重試的時間間隔
- exponential()指數(shù)級增長的時間間隔
- fix()表示固定的時間間隔
歡迎關(guān)注我的博客物延,里面有很多精品合集
- 本文轉(zhuǎn)載注明出處(必須帶連接,不能只轉(zhuǎn)文字):字母哥博客仅父。
覺得對您有幫助的話叛薯,幫我點贊、分享笙纤!您的支持是我不竭的創(chuàng)作動力耗溜! 。另外省容,筆者最近一段時間輸出了如下的精品內(nèi)容抖拴,期待您的關(guān)注。