Spring Boot WebClient Connection and Read Timeout
Asked Answered
B

3

5

Currently my post and get requests are handled through WebClients which has a common connection and read timeout in Spring Boot. I have 5 different classes each requiring its own set of connection and read timeout. I don't want to create 5 different WebClients, rather use the same Webclient but while sending a post or a get request from a particular class, specify the required connection and read timeout. Is there any way to implement this?

My current WebClient:

    @Bean
    public WebClient getWebClient(WebClient.Builder builder){

        HttpClient httpClient = HttpClient.newConnection()
                .tcpConfiguration(tcpClient -> {
                    tcpClient = tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeout*1000);
                    tcpClient = tcpClient.doOnConnected(conn -> conn
                            .addHandlerLast(new ReadTimeoutHandler(readTimeout, TimeUnit.SECONDS)));
                    return tcpClient;
                }).wiretap(true);

        ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);

        return builder.clientConnector(connector).build();
    }

A post request I'm using:

public WebClientResponse httpPost(String endpoint, String requestData, Map<String, Object> requestHeader) {

        ClientResponse res = webClient.post().uri(endpoint)
                .body(BodyInserters.fromObject(requestData))
                .headers(x -> {
                    if(requestHeader != null && !requestHeader.isEmpty()) {
                        for (String s : requestHeader.keySet()) {
                            x.set(s, String.valueOf(requestHeader.get(s)));
                        }
                    }
                })
                .exchange()
                .doOnSuccess(x -> log.info("response code = " + x.statusCode()))
                .block();

        return convertWebClientResponse(res);
    }
Burlie answered 16/11, 2021 at 7:3 Comment(1)
You can have timeout members in WebClient and its respective setter methods. Before sending each request, you can change the timeout values using setters and then call appropriate Get and Post requests.Barmen
F
6

You can configure request-level timeout in WebClient.

 webClient.get()
   .uri("https://baeldung.com/path")
   .httpRequest(httpRequest -> {
   HttpClientRequest reactorRequest = httpRequest.getNativeRequest();
   reactorRequest.responseTimeout(Duration.ofSeconds(2));
 });

Now what you can do is that based on the request you can add those values either from the properties file or you can hard code them.

Reference:- https://www.baeldung.com/spring-webflux-timeout

Fusionism answered 16/11, 2021 at 7:11 Comment(4)
so in Duration.ofSeconds() I pass the required timeout. But in my configuration I added tcpClient options for connection and read timeout, I'm still unsure how to add that using thisBurlie
That is your default timeout. This will override them.Fusionism
yes but I need tcp level timeouts. It's not the same thing rightBurlie
it's the same. the request will open a TCP connection using the provided configuration. You can add some sort of strategy pattern as well to modify this.Fusionism
H
0

You can try timeout with webClient like below,

webClient.post()
   .uri(..)
   .body(..)
   .retrieve()
   .
   .
   .
   .timeout(Duration.ofMillis(30);

30 is just example.

Halverson answered 4/6, 2022 at 8:55 Comment(3)
This is a timeout executed by the Reactor stack, not by Spring WebClient. It has nothing to do with timeouts regarding HTTP requests.Chirography
Thanks for that clarification, @KrzysztofTomaszewski . The book Cloud Native Spring in Action (Manning) on page 281 make it look like this sort of thing sets up a timeout for the GET request itself. Looks like the book needs to be corrected/clarified. I wonder what Reactor does when the timeout is reached, though; does it cancel the underlying HTTP request somehow?Burr
I'm curious about it too.Chirography
S
0
@Bean
public WebClient getWebClient() {
    HttpClient httpClient = HttpClient.create()
            .tcpConfiguration(client ->
                    client.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 4000)
                            .doOnConnected(conn -> conn
                                    .addHandlerLast(new ReadTimeoutHandler(4)) 
                                    .addHandlerLast(new WriteTimeoutHandler(4))));
    ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient.wiretap(true));
    return WebClient.builder()
            .clientConnector(connector)
            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) // if you need a default header
            .build();
}

or you can use what @Amol has suggested

Septuagenarian answered 16/11, 2022 at 14:56 Comment(1)
change ReadTimeoutHandler and WriteTimeoutHandler values doesn't change read\write timeouts from default value 10secGoatee

© 2022 - 2024 — McMap. All rights reserved.