在Spring WebFlux webclient中设置超时

时间:2018-02-26 16:35:23

标签: java spring spring-boot timeout spring-webflux

我正在使用Spring Webflux WebClient从我的Spring启动应用程序进行REST调用。并且每次都在30秒内超时。

以下是我尝试在Spring webfulx的WebClient中设置套接字超时的一些代码。

 - ReactorClientHttpConnector connector = new ReactorClientHttpConnector(options -> options
           .option(ChannelOption.SO_TIMEOUT, 600000).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 600000));
 - ReactorClientHttpConnector connector = new ReactorClientHttpConnector(
           options -> options.afterChannelInit(chan -> {
                chan.pipeline().addLast(new ReadTimeoutHandler(600000));
            }));
 - ReactorClientHttpConnector connector1 = new ReactorClientHttpConnector(options -> options
            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 600000).afterNettyContextInit(ctx -> {
                ctx.addHandlerLast(new ReadTimeoutHandler(600000, TimeUnit.MILLISECONDS));
            }));

并尝试使用“clientConnector”方法在“WebClient”中添加上面的连接器设置。

还尝试将超时设置如下:

webClient.get().uri(builder -> builder.path("/result/{name}/sets")
                    .queryParam("q", "kind:RECORDS")
                    .queryParam("offset", offset)
                    .queryParam("limit", RECORD_COUNT_LIMIT)
                    .build(name))
            .header(HttpHeaders.AUTHORIZATION, accessToken)
            .exchange().<b><i>timeout(Duration.ofMillis(600000))</i></b>
            .flatMap(response -> handleResponse(response, name, offset));

上述选项均不适合我。

我正在使用org.springframework.boot:spring-boot-gradle-plugin:2.0.0.M7,它们具有org.springframework的依赖性:spring-webflux:5.0.2.RELEASE。

请在这里建议,如果我在这里做错了,请告诉我。

4 个答案:

答案 0 :(得分:11)

我已经尝试过复制这个问题而且不能。使用reactor-netty 0.7.5.RELEASE。

我不确定你在说什么超时。

可以使用ChannelOption.CONNECT_TIMEOUT_MILLIS配置连接超时。我在“连接”日志消息和实际错误之间得到10秒:

WebClient webClient = WebClient.builder()
    .clientConnector(new ReactorClientHttpConnector(options -> options
        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)))
    .build();

webClient.get().uri("http://10.0.0.1/resource").exchange()
    .doOnSubscribe(subscription -> logger.info("connecting"))
    .then()
    .doOnError(err -> logger.severe(err.getMessage()))
    .block();

如果您正在谈论读/写超时,那么您可以查看Netty的ReadTimeoutHandlerWriteTimeoutHandler

完整示例可能如下所示:

ReactorClientHttpConnector connector = new ReactorClientHttpConnector(options ->
        options.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10)
                .onChannelInit(channel -> {
                        channel.pipeline().addLast(new ReadTimeoutHandler(10))
                                .addLast(new WriteTimeoutHandler(10));
                return true;
        }).build());

从Reactor Netty 0.8和Spring Framework 5.1开始,配置现在如下所示:

TcpClient tcpClient = TcpClient.create()
                 .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000)
                 .doOnConnected(connection ->
                         connection.addHandlerLast(new ReadTimeoutHandler(10))
                                   .addHandlerLast(new WriteTimeoutHandler(10)));
WebClient webClient = WebClient.builder()
    .clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)))
    .build();

也许在您的application.properties中添加以下内容可以提供有关HTTP级别所发生情况的更多信息:

logging.level.reactor.ipc.netty.channel.ContextHandler=debug
logging.level.reactor.ipc.netty.http.client.HttpClient=debug

答案 1 :(得分:2)

因为HttpClient.from(tcpClient)在最新的netty中已弃用(v0.9.x,并将在v1.1.0中删除)。您可以使用responseTimeout()并忽略在其他代码中看到的太多HTTP连接配置,并且此实现可用于旧的和新的。

创建HttpClient

HttpClient httpClient = HttpClient.create().responseTimeout(Duration.ofMillis(500)); // 500 -> timeout in millis

使用webClient构建器fxn .clientConnector()

将httpClient添加到webclient
WebClient
.builder()
.baseUrl("http://myawesomeurl.com")
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();

此外,网站上提供的大多数实现都不会被弃用,以确保您没有使用已弃用的实现,可以查看此link

仅供参考:对于较旧的netty版本(即responseTimeout()在幕后使用tcpConfiguration(),新版本不推荐使用。但是responseTimeout()在> = v0.9.11中使用了新的实现,因此即使将来更改项目的网络版本,代码也不会中断。

注意:如果您使用的是春季默认情况下默认提供的旧Netty版本,则可能也可以使用Brian的实现。 (虽然不确定)

如果您想了解更多有关responseTimeout()及其工作原理的信息,可以检查源代码herehere或github要旨here

答案 2 :(得分:1)

@im_bhatman 提到的每个 HttpClient.from(TcpClient) 方法已被弃用,这里有一种使用旧方法的方法。

// works for Spring Boot 2.4.0, 2.4.1, and 2.4.2
// doesn't work for Spring Boot 2.3.6, 2.3.7, and 2.3.8
HttpClient httpClient = HttpClient.create()
        //.responseTimeout(Duration.ofSeconds(READ_TIMEOUT_SECONDS))
        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECT_TIME_MILLIS)
        .doOnConnected(c -> {
                c.addHandlerLast(new ReadTimeoutHandler(READ_TIMEOUT_SECONDS))
                 .addHandlerLast(new WriteTimeoutHandler(WRITE_TIMEOUT_SECONDS));
        });
ClientConnector clientConnector = new ReactorClientHttpConnector(httpClient);
WebClient webClient = WebClient.builder()
        .clientConnector(clientConnector)
        ...
        .build();

我不确定

  • HttpClient#responseTimeout(...)
  • HttpClient#doOnConnected(c -> c.addHandler(new ReadTimeoutHandler(...)))

答案 3 :(得分:0)

请参考下面的代码块以设置超时并使用webclient重试

.retrieve()
            .onStatus(
                   (HttpStatus::isError), // or the code that you want
                    (it -> handleError(it.statusCode().getReasonPhrase())) //handling error request
           )
            .bodyToMono(String.class)
            .timeout(Duration.ofSeconds(5))
            .retryWhen(
                    Retry.backoff(retryCount, Duration.ofSeconds(5))
                            .filter(throwable -> throwable instanceof TimeoutException)
           )