如何在Spring WebClient中设置和处理超时?

时间:2019-02-17 12:12:20

标签: java spring spring-webflux project-reactor reactive

Spring文档说,需要为WebClient手动配置http客户端以设置超时时间:https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-client-builder-reactor-timeout。 但是由于WebClient返回反应式Mono,因此可以(在API上)应用.timeout方法。

效果一样吗?

此外,当使用.timeout方法时,应使用Reactor的TimeoutException。如果手动完成配置,例如doOnError(TimeoutException.class, ...)是否可以工作,流中是否会出现相同的错误?

1 个答案:

答案 0 :(得分:1)

我的发现

以http客户端特定的方式设置超时将导致http客户端特定的异常,即WebClient不会包装异常:

@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()会导致常规的TimeoutExceptionjava.util.concurrent),但是Web客户端是否以后会处理连接(可能不是)仍然是一个悬而未决的问题。

我的解决方案是使用http客户端特定的配置,以确保本机和正确的方式来利用连接,同时添加新的处理程序,该处理程序将与http客户端相关的异常包装为更通用的异常(或java.util.concurrent.TimeoutException),以便WebClient客户将不会依赖提供程序异常。