如何在 Spring WebClient 中设置和处理超时?
Spring 文档说需要手动为 WebClient 配置 http 客户端以设置超时:https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-客户端构建器反应器超时.但是由于 WebClient 返回反应式 Mono,因此可以(api-wise)应用 .timeout
方法.
Spring docs says it is required to configure http client for WebClient manually to set timeouts: https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-client-builder-reactor-timeout.
But since WebClient returns reactive Mono, it's possible (api-wise) to apply .timeout
method.
效果一样吗?
此外,当使用 .timeout
方法时,Reactor 的 TimeoutException
是预期的.如果手动完成配置,流中是否会出现相同的错误,即 doOnError(TimeoutException.class, ...)
是否有效?
Moreover, when one uses .timeout
method, Reactor's TimeoutException
is expected. Will the same error appear in the stream if configuration is done manually i.e. will doOnError(TimeoutException.class, ...)
work?
我的发现
以特定于 http 客户端的方式设置超时将导致特定于 http 客户端的异常,即 WebClient
不包装异常:
Setting a timeout in a http client specific way will lead to http client specific exception i.e. WebClient
doesn't wrap exceptions:
@Test
void test() {
var host = "localhost";
var endpoint = "/test";
var port = 8089;
var timeout = Duration.ofSeconds(3);
WireMockServer wireMockServer = new WireMockServer(wireMockConfig().port(8089));
wireMockServer.start();
WireMock.configureFor(host, wireMockServer.port());
WireMock.stubFor(get(urlEqualTo(endpoint))
.willReturn(aResponse().withFixedDelay((int) timeout.toMillis())));
HttpClient httpClient = HttpClient.create()
.tcpConfiguration(client ->
client.doOnConnected(conn -> conn
.addHandlerLast(new ReadTimeoutHandler((int) (timeout.toSeconds() / 2)))
.addHandlerLast(new WriteTimeoutHandler((int) (timeout.toSeconds() / 2)))));
WebClient webClient = WebClient.builder()
.baseUrl(format("http://%s:%d", host, port))
.clientConnector(new ReactorClientHttpConnector(httpClient)).build();
webClient.get().uri(endpoint).retrieve().bodyToMono(Recommendation.class).block();
}
这会导致io.netty.handler.timeout.ReadTimeoutException
.
.timeout(timeout.dividedBy(2)).block()
导致常规的 TimeoutException
(java.util.concurrent
) 但是Web 客户端是否会在之后处理连接(可能不会)仍然是一个悬而未决的问题.
.timeout(timeout.dividedBy(2)).block()
leads to regular TimeoutException
(java.util.concurrent
) but it's still an open question whether a web client takes care about connections afterwards (probably not).
我的解决方案是使用 http 客户端特定的配置来确保使用连接的本机和正确方式,同时添加新的处理程序,将 http 客户端相关的异常包装成更通用的异常(或 java.util.concurrent.TimeoutException
) 以便 WebClient
客户端不会依赖提供程序异常.
My solution is to use http client specific configuration to ensure native and correct way to utilize connections while adding new handler that wraps http client related exception into more generic ones (or java.util.concurrent.TimeoutException
) so that WebClient
clients won't depend on provider exceptions.