1. 概述
Spring 5 引入了一個全新的框架——Spring WebFlux,它支持我們在 Web 應用程序中進行響應式編程。為了執行 HTTP 請求,我們可以使用 WebClient 接口,該接口基於 Reactor 項目提供了一種功能性 API。
在本教程中,我們將重點關注 我們的 WebClient 的超時設置。我們將討論不同的方法,如何正確設置不同的超時時間,既可以全局在整個應用程序中設置,也可以針對單個請求進行設置。
2. WebClient 和 HTTP 客户端
在繼續之前,我們先進行一個快速回顧。Spring WebFlux 包含自己的客户端,即 WebClient 類,用於以反應式方式執行 HTTP 請求。 WebClient 也需要一個 HTTP 客户端庫才能正常工作。Spring 內置支持 某些庫,但默認使用 Reactor Netty。
大多數配置,包括超時設置,都可以使用這些客户端進行操作。
3. 通過 HTTP 客户端配置超時
正如我們之前提到的,設置應用程序中不同 WebClient 超時的最簡單方法是 通過設置底層 HTTP 客户端的全局超時。 這種方法也是最有效的。
由於 Netty 是 Spring WebFlux 的默認客户端庫,我們將使用 Reactor Netty 的 HttpClient 類 來演示我們的示例。
3.1. 響應超時
響應超時是指在發送請求後等待接收響應的時間。我們可以使用 `responseTimeout() 方法來配置客户端的響應超時時間。
HttpClient client = HttpClient.create()
.responseTimeout(Duration.ofSeconds(1)); 在此示例中,我們配置了超時時間為 1 秒。Netty 默認不設置響應超時時間。
之後,我們可以將 HttpClient 提供給 Spring 的 WebClient:
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();完成後的翻譯:
執行完上述操作後,WebClient 繼承了底層 HttpClient 提供的所有配置,應用於所有發送的請求。
3.2. 連接超時
連接超時是指在客户端和服務器之間建立連接的時間段。我們可以使用不同的通道選項鍵和 option() 方法來配置:
HttpClient client = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
// create WebClient...提供的值為毫秒,因此我們配置了超時時間為10秒。Netty默認將該值設置為30秒。
此外,我們還可以配置keep-alive選項,當連接處於空閒狀態時,將發送TCP檢查探測。
HttpClient client = HttpClient.create()
.option(ChannelOption.SO_KEEPALIVE, true)
.option(EpollChannelOption.TCP_KEEPIDLE, 300)
.option(EpollChannelOption.TCP_KEEPINTVL, 60)
.option(EpollChannelOption.TCP_KEEPCNT, 8);
// create WebClient...因此,我們已啓用心跳檢測,在空閒5分鐘後以60秒的間隔進行探測。同時,我們還設置了連接斷開前最大探測次數為8次。
當連接在特定時間內未建立或斷開時,將拋出 ConnectTimeoutException
3.3. 讀取和寫入超時
讀取超時發生在在一定時間內未讀取任何數據的情況下,而寫入超時發生在寫入操作無法在特定時間完成時。 HttpClient 允許配置額外的處理程序來配置這些超時時間:
HttpClient client = HttpClient.create()
.doOnConnected(conn -> conn
.addHandler(new ReadTimeoutHandler(10, TimeUnit.SECONDS))
.addHandler(new WriteTimeoutHandler(10)));
// create WebClient...在這種情況下,我們通過使用 doOnConnected() 方法配置了一個連接回調,其中我們創建了額外的處理程序。為了配置超時時間,我們添加了 ReadTimeOutHandler 和 WriteTimeOutHandle 實例。我們將兩者都設置為 10 秒。
這些處理程序的構造函數接受兩種參數變體。對於第一種變體,我們提供了一個帶有 TimeUnit 規格的數字,而第二種變體則將給定的數字轉換為秒。
Netty 庫底層提供了 ReadTimeoutException 和 WriteTimeoutException 類,用於處理錯誤。
3.4. SSL/TLS 超時
握手超時是指 系統在嘗試建立 SSL 連接所花費的時間長度,如果在指定時間內未能建立連接,則操作將被中止。可以通過 secure() 方法設置 SSL 配置:
HttpClient.create()
.secure(spec -> spec.sslContext(SslContextBuilder.forClient())
.defaultConfiguration(SslProvider.DefaultConfigurationType.TCP)
.handshakeTimeout(Duration.ofSeconds(30))
.closeNotifyFlushTimeout(Duration.ofSeconds(10))
.closeNotifyReadTimeout(Duration.ofSeconds(10)));
// create WebClient...如上所述,我們設置了握手超時時間為30秒(默認:10秒),同時將close_notify刷新(默認:3秒)和讀取(默認:0秒)超時時間設置為10秒。所有方法均通過SslProvider.Builder接口提供。
SslHandshakeTimeoutException用於處理由於配置的超時時間而導致的握手失敗。
3.5. 代理超時
<em/>HttpClient<em/> 也支持代理功能。如果對同伴的<strong/>連接建立嘗試在代理超時時間內未完成,則連接嘗試失敗</strong/>。 我們在 <a href="https://projectreactor.io/docs/netty/release/api/reactor/netty/transport/ClientTransport.html#proxy-java.util.function.Consumer-">proxy()</a/> 配置中設置此超時時間:
HttpClient.create()
.proxy(spec -> spec.type(ProxyProvider.Proxy.HTTP)
.host("proxy")
.port(8080)
.connectTimeoutMillis(30000));
// create WebClient...我們使用 將默認值設置為 10 秒時,設置超時時間為 30 秒。
Netty 庫還實現了自己的 以防出現任何失敗。
4. 請求級別超時
在上一節中,我們使用 HttpClient 配置了全局超時設置。但是,我們也可以獨立於全局設置,設置響應請求特定的超時時間。
4.1. 響應超時 – 使用 HttpClientRequest
正如之前所述,我們也可以在請求級別配置 響應超時:
webClient.get()
.uri("https://baeldung.com/path")
.httpRequest(httpRequest -> {
HttpClientRequest reactorRequest = httpRequest.getNativeRequest();
reactorRequest.responseTimeout(Duration.ofSeconds(2));
});在上述案例中,我們使用了 WebClient’s httpRequest() 方法來獲取底層 Netty 庫中的原生 HttpClientRequest 實例。
這種響應超時設置 會覆蓋任何在 HttpClient 級別的響應超時設置。 此外,我們還可以將其設置為 null 以移除任何先前配置的值。
4.2. 反應式超時 – 使用 Reactor 核心
Reactor Netty 使用 Reactor 核心作為其反應式流的實現。要配置其他超時,我們可以使用 timeout() 操作符,該操作符由 Mono 和 Flux 發佈器提供。
webClient.get()
.uri("https://baeldung.com/path")
.retrieve()
.bodyToFlux(JsonNode.class)
.timeout(Duration.ofSeconds(5));在那種情況下,如果在規定的5秒內沒有收到任何項,將會出現 TimeoutException。
請注意,使用 Reactor Netty 中提供的更具體的超時配置選項更好,因為它們為特定目的和用例提供了更多的控制。
timeout() 方法適用於整個操作,從建立連接到遠程對端接收響應。
它不會覆蓋任何 HttpClient 相關的設置。
5. 異常處理
我們剛剛學習了不同的超時配置。現在是時候快速瞭解一下異常處理。每種類型的超時都會產生一個專門的異常,因此我們可以輕鬆地使用 Ractive Streams 和 <em onError</em> 塊 來<strong >處理它們。
webClient.get()
.uri("https://baeldung.com/path")
.retrieve()
.bodyToFlux(JsonNode.class)
.timeout(Duration.ofSeconds(5))
.onErrorMap(ReadTimeoutException.class, ex -> new HttpTimeoutException("ReadTimeout"))
.onErrorReturn(SslHandshakeTimeoutException.class, new TextNode("SslHandshakeTimeout"))
.doOnError(WriteTimeoutException.class, ex -> log.error("WriteTimeout"))
...我們可以重用之前描述的任何異常,並使用 Reactor 編寫自定義的處理方法。
此外,我們還可以根據 HTTP 狀態碼添加一些邏輯:
webClient.get()
.uri("https://baeldung.com/path")
.onStatus(HttpStatus::is4xxClientError, resp -> {
log.error("ClientError {}", resp.statusCode());
return Mono.error(new RuntimeException("ClientError"));
})
.retrieve()
.bodyToFlux(JsonNode.class)
...6. 結論
在本教程中,我們學習瞭如何使用 Netty 示例在 Spring WebFlux 中配置 WebClient 的超時時間。
我們簡要介紹了不同的超時時間和正確設置它們的方法,包括在 HttpClient 級別以及如何將其應用於全局設置。然後,我們使用單個請求配置響應超時,在請求級別進行設置。最後,我們展示了處理髮生的異常的不同方法。