How do you configure a response timeout for WebClient, and how does it differ from other timeout types?
answer
- HttpClient.responseTimeout(Duration) -> ReactorClientHttpConnector
- clientConnector on WebClient.builder()
- connect vs response vs read/write idle
- CONNECT_TIMEOUT_MILLIS via ChannelOption
- .timeout() = whole pipeline, TimeoutException
basics
~10 sConfigure a Reactor Netty HttpClient with responseTimeout(Duration), wrap it in a ReactorClientHttpConnector, and pass it to WebClient.builder().clientConnector(...). responseTimeout limits the time waiting for the full response after the request is sent.
solid answer
~30 sWebClient's default engine is Reactor Netty. You build a `reactor.netty.http.client.HttpClient`, set timeouts on it, wrap it in a `ReactorClientHttpConnector`, and give that to `WebClient.builder().clientConnector(connector)`. The key knob is `responseTimeout(Duration)` — the max time between sending the request and receiving the response; on breach it errors the stream (Reactor Netty's `ReadTimeoutException` propagated as a connection error). Distinguish it from: connection timeout (`ChannelOption.CONNECT_TIMEOUT_MILLIS`, time to establish the TCP connection), and low-level read/write idle timeouts (`ReadTimeoutHandler`/`WriteTimeoutHandler`). A reactive alternative is `.timeout(Duration)` on the Mono, but that's a whole-pipeline timeout including retries/decoding, so prefer `responseTimeout` for the network round-trip and use per-operator `.timeout()` deliberately.
code
java · 17 linesimport reactor.netty.http.client.HttpClient;
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
HttpClient httpClient = HttpClient.create()
// TCP connect timeout
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
// time to receive the full response after request is sent
.responseTimeout(Duration.ofSeconds(3))
// low-level read idle timeout (e.g. for streaming)
.doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(3)));
WebClient webClient = WebClient.builder()
.baseUrl("https://upstream.example.com")
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();go deeper
Know that you set responseTimeout on a Reactor Netty HttpClient and wire it via ReactorClientHttpConnector.
Distinguish connect vs response vs read/write idle timeouts and their exact APIs.
Explain responseTimeout vs Reactor .timeout() placement relative to retries, and the different exception types each yields.
Set org-wide timeout budgets tied to SLAs, coordinate with connection-pool sizing, and ensure timeouts are shorter than upstream-of-you deadlines to avoid deadline inversion.
**Why timeouts matter:** without them a slow or hung upstream ties up connections and threads indefinitely, cascading into your own outages. In reactive apps a hung call also holds an event-loop resource, so bounding every external call is mandatory. **The engine:** `WebClient` delegates I/O to a `ClientHttpConnector`. The default implementation is **`ReactorClientHttpConnector`**, backed by **Reactor Netty's `HttpClient`**. To tune timeouts you configure that underlying `HttpClient`, wrap it, and inject it: ```java HttpClient httpClient = HttpClient.create() .responseTimeout(Duration.ofSeconds(3)); WebClient client = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .build(); ``` **The main timeout types and their APIs:** 1. **Connect timeout** — max time to establish the TCP connection. Set via `httpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)`. Fires when the server/host is unreachable. 2. **Response timeout** — `httpClient.responseTimeout(Duration)`. The time from finishing sending the request to receiving the response. This is the single most useful per-request budget for a network round-trip. On breach the stream errors (surfacing a Reactor Netty read timeout). It can also be set per-request via `request.responseTimeout(...)` inside `httpRequest()` customization. 3. **Read / write idle timeouts** — added as Netty handlers: `ReadTimeoutHandler` / `WriteTimeoutHandler` via `httpClient.doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(3)))`. These fire when no data is read/written for the interval — useful for streaming responses where a single 'response' can be long-lived. 4. **SSL handshake timeout** — configured on the SSL provider. **Reactive `.timeout(Duration)`:** Reactor's `Mono.timeout()`/`Flux.timeout()` is an *operator-level* timeout on the assembled pipeline. It's convenient but covers *everything downstream of where you place it* — connection, response, body decoding, and any `retryWhen` above it. If you put `.timeout()` above `.retryWhen()`, it bounds the *entire* retry sequence; below it, each attempt. Because of that ambiguity, prefer `responseTimeout` for the transport round-trip and reserve `.timeout()` for an explicit overall deadline. Also, `.timeout()` throws `java.util.concurrent.TimeoutException`, while `responseTimeout` surfaces a Netty read-timeout error — different exception types to match in `onStatus`/`onErrorResume`/retry predicates. **Gotchas:** - Setting `.timeout()` on the Mono does NOT cancel the underlying TCP work cleanly in all cases the way `responseTimeout` does; Reactor Netty's `responseTimeout` is transport-aware. - A too-aggressive `responseTimeout` combined with retries can amplify load on an already-struggling upstream (see retry question). - Timeouts interact with the **connection pool**: a timed-out request releases (or evicts) its pooled connection; misconfiguration can exhaust the pool. - For blocking clients people used `RestTemplate` + `ClientHttpRequestFactory` timeouts; the reactive equivalent is exactly this `HttpClient` config. **When to use which:** always set connect + response timeouts. Add read/write idle timeouts for streaming/SSE. Use `.timeout()` only for a deliberate end-to-end SLA cap.
- How does responseTimeout differ from putting .timeout(Duration) on the returned Mono?responseTimeout is transport-level (request-sent -> response-received) inside Reactor Netty and surfaces a Netty read-timeout. .timeout() is a Reactor operator covering the whole assembled pipeline downstream of it (connect + response + decode + any retries above), throwing java.util.concurrent.TimeoutException. Use responseTimeout for the round-trip, .timeout() for an explicit overall deadline.
- You configured responseTimeout but connections still hang when the host is down. Why?responseTimeout only starts after the request is sent. If the TCP connection can't even be established (host unreachable), you need the connect timeout via ChannelOption.CONNECT_TIMEOUT_MILLIS. The two cover different phases.
saying these in an interview costs you the question
- Thinking a single .timeout() replaces the need for connect/response timeouts.
- Believing responseTimeout covers TCP connection establishment (it starts after the request is sent).
- Configuring timeouts on WebClient directly with no ClientHttpConnector — the knobs live on the underlying HttpClient.
- Assuming responseTimeout throws TimeoutException (it surfaces a Reactor Netty read-timeout error).