skip to content

How do you configure connect and read timeouts for a load-balanced Feign client, and what's the difference?

level: seniorimportance: should knowfreq 50%

answer

  1. connectTimeout = open TCP; readTimeout = await response
  2. spring.cloud.openfeign.client.config.<name>.*
  3. reserved name 'default' = all clients
  4. milliseconds; per-attempt not total
  5. defaults ~10s — set them explicitly

basics

~10 s

Set spring.cloud.openfeign.client.config.<clientName>.connectTimeout and readTimeout (milliseconds); use client name 'default' for all clients. connectTimeout limits establishing the TCP connection; readTimeout limits waiting for the response after the request is sent.

solid answer

~40 s

Feign exposes two timeouts via Request.Options: connectTimeout (time to open the TCP connection to the chosen instance) and readTimeout (time to wait for the response once connected). Configure them per client with properties: spring.cloud.openfeign.client.config.order-service.connectTimeout=2000 and readTimeout=5000 (milliseconds). Use the reserved name default to apply to every client, then override per client. You can also define a Request.Options @Bean in a per-client configuration class. Defaults have historically been generous (on the order of 10s), so setting explicit, tight timeouts is important — an unset read timeout lets a slow instance tie up threads. Note these are the HTTP client's timeouts; they're independent of load-balancer instance selection and of any Resilience4j time limiter you layer on top.

code

java · 23 lines
java
// application.yml
// spring:
//   cloud:
//     openfeign:
//       client:
//         config:
//           default:
//             connectTimeout: 1000
//             readTimeout: 3000
//           order-service:
//             connectTimeout: 2000
//             readTimeout: 5000

// Or in a per-client config class (kept out of component scan):
public class OrderFeignConfig {
    @Bean
    Request.Options options() {
        return new Request.Options(
            2000, TimeUnit.MILLISECONDS,   // connect
            5000, TimeUnit.MILLISECONDS,   // read
            true);                          // followRedirects
    }
}

go deeper

for a junior

Know there are connect and read timeouts and they're set via properties in ms.

for a middle

Give the property keys, the default client name, and the connect-vs-read distinction.

for a senior

Explain per-attempt semantics, why defaults are dangerous, and thread-pool exhaustion.

for a principal

Coordinate timeouts with retries, circuit breakers, and Resilience4j time limiters; base values on measured p99 latency.

**The two timeouts.** Feign's `Request.Options` holds: - **`connectTimeout`** — maximum time to **establish the TCP connection** to the target host:port (the instance SCLB picked). Exceeding it throws a connect timeout — the server may be down/unreachable. - **`readTimeout`** — maximum time to **wait for response data** after the request has been sent and the connection established. Exceeding it means the server accepted the connection but is too slow to respond (a stuck/overloaded instance). **Property-based config (preferred).** ``` spring.cloud.openfeign.client.config.order-service.connectTimeout=2000 spring.cloud.openfeign.client.config.order-service.readTimeout=5000 ``` Values are **milliseconds**. The reserved client name **`default`** sets values for **all** clients: ``` spring.cloud.openfeign.client.config.default.connectTimeout=1000 spring.cloud.openfeign.client.config.default.readTimeout=3000 ``` A named client's values override the `default` block. > Prefix note: modern Spring Cloud uses `spring.cloud.openfeign.client.config.*`; older versions used `feign.client.config.*`. Same keys underneath. **Code-based config.** In a per-client configuration class (kept out of the component scan), define: ```java @Bean Request.Options options() { return new Request.Options(2000, TimeUnit.MILLISECONDS, 5000, TimeUnit.MILLISECONDS, true); } ``` By default **properties override this bean** (`default-to-properties=true`). **Why it matters.** Default Feign timeouts are relatively long (historically ~10 seconds connect and read). Under load a slow downstream instance with no tight `readTimeout` will pin request-handling threads, cascade backpressure, and can exhaust the caller's thread/connection pool — a classic outage amplifier. Explicit, conservative timeouts are a resilience baseline; pair them with retries (to a *different* instance) and a circuit breaker. **Interactions & gotchas.** - Timeouts are **per attempt**. If load-balancer retry (`spring-cloud-starter-loadbalancer` + `spring-retry`) is enabled, each retry gets its own connect/read window, so total wall-clock can be a multiple — size retry counts accordingly. - The underlying HTTP client matters: Feign can run on `Client.Default` (HttpURLConnection), Apache HttpClient 5, or OkHttp. Feign applies these timeouts through `Request.Options` regardless, but connection-pool settings (max connections, keep-alive) are separate and also affect latency. - These are **transport** timeouts. A Resilience4j `TimeLimiter` (used with reactive/async wrappers) is a **separate** overall deadline; don't confuse the two. - A `readTimeout` shorter than a legitimately slow endpoint causes spurious failures — measure real latencies (p99) before tightening.

  • You enabled load-balancer retries and see total request latency exceed your readTimeout. Why?
    Timeouts are per attempt, not for the whole call. With N retries the worst-case wall-clock is roughly N times the per-attempt timeout, so total latency can far exceed a single readTimeout. Bound retries and consider an overall deadline.
  • What breaks if you leave readTimeout at its default under heavy load?
    A slow downstream instance can hold request threads for ~10s each, exhausting the caller's thread/connection pool and cascading the failure. Tight timeouts plus a circuit breaker prevent this.

saying these in an interview costs you the question

  • Confusing connectTimeout (open socket) with readTimeout (await response)
  • Thinking the timeout applies to the whole call including retries
  • Assuming Feign has no default timeout / it's infinite
  • Setting timeouts in seconds instead of milliseconds

context