Configure timeout for Spring WebFlux WebClient

Guide to setting timeouts in Spring WebFlux using WebClient and Netty.

Overview

This tutorial focuses on adding different types of timeouts in Spring WebFlux – WebClient. Spring WebClient is a non-blocking and reactive web HTTP client that is going to replace the RestTemplate. Having reactive processing capabilities, the WebClient allows asynchronous communication with other services.

With this tutorial, your will learn to set timeouts in a Spring 5 Web Client. For example: response timeout, read/write timeout, SSL/TLS timeout, connection timeout, and reactive timeout.

Why to Set Request Timeouts?

Timeouts are extremely important part of a communication. Especially when we deal with external resources. For example a database or a downstream service. When client makes a request, it doesn’t have any control over the server or the network behaviour. Thus, it just waits for the server to respond. However, if the response is taking too long a client needs to decide when to stop waiting.

Long awaiting clients can consume expensive resources, which otherwise can be used for other better things. That is why, it is a good practice to introduce optimal timeouts for communications.

Reactive Timeout

While working with the Spring WebFlux API, the most basic timeout we can set is the reactive timeout. This timeout is imposed by the Reactive Core API that Spring WebFlux is based upon. Both main publishers – Mono and Flux, support this timeout through their timeout(duration) method.

WebClient.create(GET_STUDENTS_URL)
    .get()
    .retrieve()
    .bodyToFlux(Student.class)
    .timeout(Duration.ofSeconds(10));Code language: Java (java)

With this timeout setting, WebFlux throws TimeoutException, if no item is arrived during the specified timeout duration of 10 seconds.

The reactive timeout is a high level timeout that is placed on the whole operation of receiving an item. So, if you want to add more specific timeouts, you should go for other options in Reactor Netty that we are going to cover next.

Response Timeout

The Response Timeout defines the maximum time a client, after sending a request, waits to receive the response. With WebClient, we can set this timeout specifically for a particular request or globally while creating a WebClient instance.

Globally using HttpClient

The WebClient internally uses Reactor Netty HttpClient to make HTTP requests. We can create an instance of HttpClient, configure required timeout on it and use it with Spring WebClient.

 WebClient webClient = WebClient
    .builder()
    .clientConnector(
        new ReactorClientHttpConnector(HttpClient
            .create()
            .responseTimeout(Duration.ofSeconds(10)))
        )
    .build();Code language: Java (java)

In the above snippet, we are creating an instance of WebClient. Also, we are configuring a custom clientConnector by providing a new instance of HttpClient. We have set a response timeout of 10 seconds on the HttpClient.

This timeout will be applicable to all requests made by using this WebClient instance.

Specific to a Request

Although we can set response timeout globally, we may want a different timeout configuration for a specific request. To do so, we need to configure the http request by accessing Reactor Netty HttpClientRequest.

webClient.create(GET_STUDENTS_URL)
    .get()
    .httpRequest(request -> {
        HttpClientRequest nativeRequest = request.getNativeRequest();
        nativeRequest.responseTimeout(Duration.ofSeconds(10));
    })
    .retrieve()
    .bodyToFlux(Student.class);Code language: Java (java)

Here, we are setting a response timeout of 10 seconds on the specific HttpClientRequest. It defines the maximum permitted duration between each read operation at the network level.

This timeout overrides any globally set response timeout – that we saw before. Also, to completely remove a globally set timeout, we can pass null as a timeout value.

Read Timeout, Write Timeout

Similar to the global response timeout we can set read timeout and write timeout on the HttpClient. First, let’s understand what these timeouts are. The Read timeout triggers when a no data is read within the specified time period. Similarly, Write timeout triggers when a write operation does not finished in the specified time.

To set these timeouts, we need to create an instance of HttpClient and use the doOnConnected callback handler. Then we will set the HttpClient instance as the WebClient’s clientConnector.

WebClient webClient = WebClient
    .builder()
    .clientConnector(
        new ReactorClientHttpConnector(HttpClient.create()
            .doOnConnected(connection -> connection
                .addHandler(new ReadTimeoutHandler(10))
                .addHandler(new WriteTimeoutHandler(10))
            )
        )
    )
    .build();Code language: Java (java)

Here, we are registering two handlers – one for Read Timeout and other for Write timeout. Having this, we get ReadTimeoutException if a read operation doesn’t start within 10 seconds after the connection is established. Or we will get WriteTimeoutException if a write operation doesn’t finish within 10 seconds after connection.

Connection Timeout

The Connection timeout happens when connection between a client and a server is not established within the specified time. This timeout is also set on the HttpClient instance. Thus, we will need to create HttpClient instance, configure timeout and set it as a clientConnector.

WebClient webClient = WebClient
    .builder()
    .clientConnector(
        new ReactorClientHttpConnector(
            HttpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
        )
    )
    .build();Code language: Java (java)

Having this a ConnectionTimeoutException will occur when no client and server connection is established within 10 seconds.

TLS Timeout

In a TLS communication both client and server first shake hands together agreeing upon encrencrypted connection. This handshake is a required step before beginning of the actual communication. We can create and use HttpClient instance to configure a SSL handshake timeout.

The SSL Handshake timeout happens when the establishment of a SSL connection takes longer than the specified timeout value. To configure this value, we need to create an instance of HttpClient and configure SSL configuration.

HttpClient.create()
    .secure(spec -> {
        spec.sslContext(SslContextBuilder.forClient().build())
            .handshakeTimeout(Duration.ofSeconds(10))
            .closeNotifyFlushTimeout(Duration.ofSeconds(5))
            .closeNotifyReadTimeout(Duration.ofSeconds(5));
    });Code language: Java (java)

Here, we have set a SSL Handshake timeout of 10 seconds, close_notify flush timeout of 5 seconds, and close_notify read timeout of 5 seconds.

Summary

In this tutorial we learned to configure timeout values in Spring WebFlux WebClient. We understood that the reactive timeout, which is based on per request basis, is a high level timeout that is applied to overall operation. However, there are other specific timeout configurations – Read Write Timeout, Connection Timeout, Response Timeout, SSL/TLS Timeout – that we can use for a more granular control. All of these timeouts are set on Reactor Netty HttpClient instance.