精講響應式WebClient第5篇-請求超時設定與異常處理

字母哥部落格發表於2020-08-24

本文是精講響應式WebClient第5篇,前篇的blog訪問地址如下:

本文來為大家介紹一下,當WebClient請求發生異常的時候,該如何處理。為了講解異常處理,我們需要先製造出異常,所以我們先為大家介紹:請求超時時長的設定。

一、請求超時時長的設定

要想模擬超時異常,我們首先要知道超時時長的正常配置渠道是怎麼樣的。如下文程式碼所示:

  • ChannelOption.CONNECT_TIMEOUT_MILLIS用來設定連線超時時長,單位是毫秒
  • ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS)用來設定讀資料超時時長,單位是毫秒
  • WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS)用來設定寫資料超時時長,單位是毫秒
//初始化一個WebClient
private WebClient getWebClient(){
   TcpClient tcpClient = TcpClient
               .create()
               .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
               .doOnConnected(connection -> {
                  connection.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS));
                  connection.addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS));
               });

   return WebClient.builder()
               .baseUrl("http://jsonplaceholder.typicode.com")
               .clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)))
               .build();
}

當我們把連線超時時長設定為5(毫秒)的時候,則連線肯定會超時。隨便傳送一個請求,超時之後會丟擲ConnectTimeoutException

當我們把讀資料超市時長設定為5(毫秒)的時候,則資料讀操作肯定會超時。隨便傳送一個請求,超時之後會丟擲ReadTimeoutException

二、處理特定的異常

下面我們就以ConnectTimeoutException為例,進行異常處理

//製造異常,將超時時間設定為5毫秒
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5)

然後執行下面的GET請求,上文WebClient的baseurl為:"http://jsonplaceholder.typicode.com" ,該網站是一個免費提供HTTP服務端測試的網站。

@Test
public void testSimple() throws Exception {
   Mono<String> mono = getWebClient()
               .get()    // 傳送GET 請求
               .uri("/posts/1")  //服務請求路徑,基於baseurl
               .retrieve() // 獲取響應體
               .bodyToMono(String.class) //響應資料型別轉換
                //進行異常處理
               .doOnError(ConnectTimeoutException.class, err -> {
                  System.out.println("發生錯誤:" +err.getMessage() );
               });
   System.out.println(mono.block());
}

上文中的doOnError是我們本節為大家介紹的異常處理方法,用於處理ConnectTimeoutException,輸出結果如下:

從輸出結果上看:一:異常得到處理,因為看到了System.out列印日誌。二是異常仍然被丟擲了,沒有得到返回值。

三、請求異常給出預設返回值

從第二小節中的程式碼及控制檯輸出,可以看出HTTP 客戶端請求沒有得到返回值,而是繼續把異常對外丟擲。假如我們目前的需求是,不論請求成功失敗,都給客戶端一個返回值,該怎麼做?也就是說我們需要在請求發生異常的時候,給出預設返回值。

@Test
public void testReturn() throws Exception {
   Mono<String> mono = getWebClient()
               .get()    // 傳送GET 請求
               .uri("/posts/1")  //服務請求路徑,基於baseurl
               .retrieve() // 獲取響應體
               .bodyToMono(String.class) //響應資料型別轉換
               .doOnError(ConnectTimeoutException.class, err -> {
                  System.out.println("發生錯誤:" +err.getMessage() );
               })
               .onErrorReturn("請求發生異常,請檢查!");
   System.out.println(mono.block());
}

使用onErrorReturn();給出請求的預設返回值,輸出結果如下:

可以看到請求測試用例成功pass了,因為我們給出了異常處理的預設返回值,沒有把異常繼續丟擲。

四、分類異常處理

上面的異常處理方法,只能處理指定的某種異常:ConnectTimeoutException。如果說我們想讓異常處理相對通用一些該怎麼辦?有的小夥伴可能會想到攔截異常的父類Exception,當然這也是一種辦法。

.doOnError(Exception.class, err -> {
   System.out.println("發生錯誤:" +err.getMessage() );
});

我們下面為大家介紹一種,針對HTTP 響應異常處理更友好的一種方式。通常來說,異常可以分為兩種:

  • 一種是客戶端輸入或訪問異常,比如:訪問的資源不存在404,沒有許可權訪問資源403,輸入的資料不符合格式等等。這種異常通常是使用者訪問了不該訪問的資源,或者輸入了不該輸入的資料導致的。通常用HTTP狀態碼錶示在400-499範圍內。
  • 另一種是服務端內部錯誤,比如:500服務內部錯誤、502閘道器錯誤等等。這種異常通常和使用者沒什麼關係,是IT基礎設施或者程式設計導致的異常。

所以我們只需要針對上面的兩類異常進行處理即可。如下文程式碼所示:

  • e.is4xxClientError()表示的是400-499狀態碼段的異常
  • e.is5xxClientError()表示的是500-599狀態碼段的異常
public void testSimple2() throws Exception {
   Mono<String> mono = getWebClient()
               .get()    // 傳送GET 請求
               .uri("/postss/1")  //服務請求路徑,基於baseurl
               .retrieve() // 獲取響應體
               .onStatus(e -> e.is4xxClientError(), resp -> {
                  System.out.println("發生客戶端輸入錯誤:" + resp.statusCode().value() + " "
                              + resp.statusCode().getReasonPhrase());
                  return Mono.error(new RuntimeException("請求失敗"));
               })
               .onStatus(e -> e.is5xxServerError(), resp -> {
                  System.out.println("發生服務端錯誤:" + resp.statusCode().value() + " "
                              + resp.statusCode().getReasonPhrase());
                  return Mono.error(new RuntimeException("伺服器異常"));
               })
               .bodyToMono(String.class); //響應資料型別轉換
   System.out.println(mono.block());
}

現在我們將請求地址由正確的"/posts/1",改成錯誤的"/postss/1",所以當我們訪問服務端的時候,服務端並不存在這個資源。異常處理的輸出結果如下:

歡迎關注我的部落格,裡面有很多精品合集

  • 本文轉載註明出處(必須帶連線,不能只轉文字):字母哥部落格

覺得對您有幫助的話,幫我點贊、分享!您的支援是我不竭的創作動力! 。另外,筆者最近一段時間輸出瞭如下的精品內容,期待您的關注。

相關文章