27

我正在尝试在我的 WebClient 上设置超时,这是当前代码:

SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();

ClientHttpConnector httpConnector = new ReactorClientHttpConnector(opt -> {
    opt.sslContext(sslContext);
    HttpClientOptions option = HttpClientOptions.builder().build();
    opt.from(option);
});
return WebClient.builder().clientConnector(httpConnector).defaultHeader("Authorization", xxxx)
                .baseUrl(this.opusConfig.getBaseURL()).build();

我需要添加超时和池策略,我在想这样的事情:

PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(this.applicationConfig.getHttpClientMaxPoolSize());
cm.setDefaultMaxPerRoute(this.applicationConfig.getHttpClientMaxPoolSize());
cm.closeIdleConnections(this.applicationConfig.getServerIdleTimeout(), TimeUnit.MILLISECONDS);

RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(this.applicationConfig.getHttpClientSocketTimeout())
        .setConnectTimeout(this.applicationConfig.getHttpClientConnectTimeout())
        .setConnectionRequestTimeout(this.applicationConfig.getHttpClientRequestTimeout()).build();

CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).setConnectionManager(cm).build();

但我不知道如何在我的网络客户端中设置 httpClient

4

10 回答 10

26

要设置读取和连接超时,我使用以下方法,因为 SO_TIMEOUT 选项不适用于使用 NIO 的通道(并发出警告Unknown channel option 'SO_TIMEOUT' for channel '[id: 0xa716fcb2]'

ReactorClientHttpConnector connector = new ReactorClientHttpConnector(
          options -> options.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
                            .compression(true)
                            .afterNettyContextInit(ctx -> {
                                ctx.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS));
                            }));
return WebClient.builder()
                .clientConnector(connector)
                .build();
于 2017-11-07T07:55:10.343 回答
23

ReactorClientHttpConnector API 在Spring WebFlux 5.1版本中更改。

所以我执行以下操作(Kotlin 语法,基于 @joshiste 示例):

val tcpClient = TcpClient.create()
    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000)
    .doOnConnected { connection ->
        connection.addHandlerLast(ReadTimeoutHandler(10))
            .addHandlerLast(WriteTimeoutHandler(10))
    }

val myWebClient = webClientBuilder
    .clientConnector(ReactorClientHttpConnector(HttpClient.from(tcpClient)))
    .baseUrl(myEndPoint)
    .build()

2021 年更新

HttpClient.from 在 Reactive Netty 的最新版本中已弃用。它正在复制 tcpClient 的配置。现在我们可以直接配置httpClient了。

val httpClient = HttpClient.create()
    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000)
    .doOnConnected { connection ->
        connection.addHandlerLast(ReadTimeoutHandler(10))
            .addHandlerLast(WriteTimeoutHandler(10))
    }

val myWebClient = webClientBuilder
    .clientConnector(ReactorClientHttpConnector(httpClient))
    .baseUrl(myEndPoint)
    .build()
于 2018-12-14T13:51:15.307 回答
15

随着 Spring Webflux 的更新,这里有一个适用于 Java 的解决方案(基于Kotlin的答案):

TcpClient timeoutClient = TcpClient.create()
    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, SECONDS*1000)
    .doOnConnected(
        c -> c.addHandlerLast(new ReadTimeoutHandler(SECONDS))
              .addHandlerLast(new WriteTimeoutHandler(SECONDS)));
return webClientBuilder.baseUrl(YOUR_URL)
       .clientConnector(new ReactorClientHttpConnector(HttpClient.from(timeoutClient)))
       .build();

2021 年更新

因为HttpClient.from(TcpClient)现在已弃用,所以更容易:

return WebClient.builder()
                .baseUrl(YOUR_URL)
                .clientConnector(new ReactorClientHttpConnector(HttpClient.create()
                                                                          .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, SECONDS * 1000)
                                                                          .doOnConnected(c -> c.addHandlerLast(new ReadTimeoutHandler(SECONDS))
                                                                                               .addHandlerLast(new WriteTimeoutHandler(SECONDS)))))
                .build();
于 2019-04-01T19:48:35.750 回答
13

WebFluxWebClient不使用 Apache Commons HTTP 客户端。尽管您可能能够通过 custom 实现一种解决方案ClientHttpConnector。现有ReactorClientHttpConnector的基于Netty。因此,考虑使用 Netty 选项来配置客户端,例如:

ReactorClientHttpConnector connector =
            new ReactorClientHttpConnector(options ->
                    options.option(ChannelOption.SO_TIMEOUT, this.applicationConfig.getHttpClientConnectTimeout()));

或者

.onChannelInit(channel -> channel.config().setConnectTimeoutMillis(this.applicationConfig.getHttpClientConnectTimeout()))

更新

我们也可以使用ReadTimeoutHandler

.onChannelInit(channel -> 
        channel.pipeline()
           .addLast(new ReadTimeoutHandler(this.applicationConfig.getHttpClientConnectTimeout())))
于 2017-09-15T14:47:53.550 回答
4

使用 Spring Webflux 5.1.8,我执行多个使用WebClient.

强制关闭其注册任务未被事件循环接受的通道
未能提交监听通知任务。事件循环关闭?

添加连接提供程序和循环资源解决了我的问题:

final ConnectionProvider theTcpClientPool = ConnectionProvider.elastic("tcp-client-pool");
final LoopResources theTcpClientLoopResources = LoopResources.create("tcp-client-loop");

final TcpClient theTcpClient = TcpClient
    .create(theTcpClientPool)
    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
    .runOn(theTcpClientLoopResources)
    .doOnConnected(theConnection -> {
        theConnection.addHandlerLast(new ReadTimeoutHandler(mTimeoutInMillisec, TimeUnit.MILLISECONDS));
        theConnection.addHandlerLast(new WriteTimeoutHandler(mTimeoutInMillisec, TimeUnit.MILLISECONDS));
    });

WebClient theWebClient = WebClient.builder()
    .baseUrl(mVfwsServerBaseUrl)
    .clientConnector(new ReactorClientHttpConnector(HttpClient.from(theTcpClient)))
    .build();
于 2019-08-01T11:40:48.517 回答
3

这是我的做法(感谢@Artem)

SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();

        ClientHttpConnector httpConnector = new ReactorClientHttpConnector(options -> {
            options.sslContext(sslContext);
            options.option(ChannelOption.SO_TIMEOUT, this.applicationConfig.getHttpClientRequestTimeout());
            options.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, this.applicationConfig.getHttpClientConnectTimeout());
            options.poolResources(PoolResources.fixed("myPool", this.applicationConfig.getHttpClientMaxPoolSize()));
        });

        return WebClient.builder().clientConnector(httpConnector).defaultHeader("Authorization", "xxxx")
                .baseUrl(this.config.getBaseURL()).build();
于 2017-09-18T14:18:40.523 回答
1

您可以使用接受 Mono 对象超时的重载 block() 方法。或者 Mono 对象上有一个 timeout() 方法可以直接使用。

WebClient webClient = WebClient.builder()
              .baseUrl( "https://test.com" )
              .defaultHeader( HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE )
              .build(); 

webClient.post()
              .uri( "/services/some/uri" )
              .body( Mono.just( YourEntityClassObject ), YourEntityClass.class )
              .retrieve()
              .bodyToMono( String.class )
              .timeout(Duration.ofMillis( 5000 )) // option 1
              .block(Duration.ofMillis( 5000 )); // option 2
于 2021-03-15T07:49:03.620 回答
1

根据上面的评论,如果你想添加一个 Socket Timeout,只需将它作为另一个选项添加到同一个 timeoutClient 中。

TcpClient timeoutClient = TcpClient.create()
    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, SECONDS*10) //Connect Timeout
    .option(ChannelOption.SO_TIMEOUT,1000) // Socket Timeout
    .doOnConnected(
        c -> c.addHandlerLast(new ReadTimeoutHandler(SECONDS))
              .addHandlerLast(new WriteTimeoutHandler(SECONDS)));
return webClientBuilder.baseUrl(YOUR_URL)
       .clientConnector(new ReactorClientHttpConnector(HttpClient.from(timeoutClient)))
       .build();
于 2019-05-31T10:15:07.173 回答
0

Kotlin 语法!!

webClient
.post()
.body(BodyInserters.fromObject(body))
.headers(headersSetter)
.retrieve()
.bodyToMono<SomeClass>()
.timeout(Duration.ofSeconds(30)) /// specify timeout duration
.doOnNext {
    logger.info{ "log something"}
}
.onErrorMap { throwable ->
    logger.error{"do some transformation"}
    throwable
}
于 2021-04-15T20:55:31.343 回答
0

WebClient.Builder您可以提供一个ReactorNettyHttpClientMapper将应用于默认值的自定义,而不是创建自己的WebClient.Builder

@Configuration
class MyAppConfiguration {

    @Bean
    fun reactorNettyHttpClientMapper(): ReactorNettyHttpClientMapper {
        return ReactorNettyHttpClientMapper { httpClient ->
            httpClient.tcpConfiguration { tcpClient ->
                tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30_000)
                    .doOnConnected { connection ->
                        connection.addHandlerLast(ReadTimeoutHandler(60))
                            .addHandlerLast(WriteTimeoutHandler(60))
                    }
            }
        }
    }
}
于 2021-01-06T09:11:35.630 回答