I'm using Spring Webflux WebClient to make a REST call from my Spring boot application. And every time getting a timeout in 30 seconds.
Here is some code I tried to set socket timeout in WebClient of Spring webfulx.
- 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));
}));
And tried to add this above connector setting in “WebClient” by using “clientConnector” method.
And also tried to set timeout as below:
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().timeout(Duration.ofMillis(600000))
.flatMap(response -> handleResponse(response, name, offset));
None of the above options is working for me.
I'm using org.springframework.boot:spring-boot-gradle-plugin:2.0.0.M7 which interally have dependecy of org.springframework:spring-webflux:5.0.2.RELEASE.
Please suggest here and let me know if I'm doing anything wrong here.
java example code the WebClient build using the default builder without any specific configuration. Hence it falls back to the default connect and read timeout, which is 30 seconds each. Modern applications do not wait for 30 seconds for anything.
WebClient is part of Spring 5's reactive web framework called Spring WebFlux. To use WebClient, you need to include the spring-webflux module in your project. Go to http://start.spring.io.
I've tried reproducing the issue and couldn't. Using reactor-netty 0.7.5.RELEASE.
I'm not sure about which timeout you're talking about.
Connection timeout can be configured with ChannelOption.CONNECT_TIMEOUT_MILLIS
. I'm getting 10 seconds between the "connecting" log message and the actual error:
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();
If you're talking about read/write timeouts, then you can look at Netty's ReadTimeoutHandler
and WriteTimeoutHandler
.
A full example could look like this:
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());
As of Reactor Netty 0.8 and Spring Framework 5.1, the configuration now looks like this:
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();
Maybe adding the following to your application.properties
will provide more information on what's happening at the HTTP level:
logging.level.reactor.ipc.netty.channel.ContextHandler=debug
logging.level.reactor.ipc.netty.http.client.HttpClient=debug
Since HttpClient.from(tcpClient)
is now deprecated in the latest netty (v0.9.x and will be removed in v1.1.0). You can use responseTimeout()
and ignore too many HTTP connection configurations which you see in other code and this implementation works with the old as well as the new one.
Create HttpClient
HttpClient httpClient = HttpClient.create().responseTimeout(Duration.ofMillis(500)); // 500 -> timeout in millis
Add httpClient to webclient using webClient builder fxn .clientConnector()
WebClient
.builder()
.baseUrl("http://myawesomeurl.com")
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
Also, the majority of the implementation available over the website is not deprecated to make sure that you are not using the deprecated one you can check out this link.
FYI: For the older netty versions (i.e < v0.9.11 version) responseTimeout()
uses tcpConfiguration() under the hood, which is deprecated in the new versions. But responseTimeout() uses the new implementation in >=v0.9.11, so even if you change the netty version of your project in the future your code is not going to break.
NOTE: If you are using the old netty version which comes by default with spring sometimes, you probably can use Brian's implementation too. (Not sure though)
If you want to read more about responseTimeout() and how does it work, you can check the source code here and here or the github gist here.
Per HttpClient.from(TcpClient)
method, as @im_bhatman mentioned, has been deprecated, here comes a way using the good-old way.
// 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();
I'm not sure about the difference of
HttpClient#responseTimeout(...)
HttpClient#doOnConnected(c -> c.addHandler(new ReadTimeoutHandler(...)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With