OpenFeign + Spring Cloud — Production Integration
Fast recall
Quick revision
Core answer
**@FeignClient**(name, url) and Spring Cloud generates an HTTP proxy. Production-grade usage requires four pillars: `RequestInterceptor` to inject auth/tracing headers, `ErrorDecoder` to translate upstream errors into typed exceptions, Resilience4j CircuitBreaker/Retry via @CircuitBreaker on the Feign bean (or Spring Cloud CircuitBreaker auto-config), and explicit `Feign.Builder` timeouts (connectTimeout, readTimeout) because the defaults are 10s/60s — far too long. Use Spring Cloud LoadBalancer (name = "payment-service" with no url) to resolve service names via Eureka/Consul/Kubernetes.Whether you can configure OpenFeign beyond the trivial @FeignClient declaration — timeouts, auth, error mapping, retry, and circuit breaker — and whether you understand how Spring Cloud LoadBalancer integrates with Feign.
Shipping @FeignClient with default 10s/60s timeouts and no circuit breaker, so one slow downstream saturates the caller's Tomcat thread pool within minutes. Also: swallowing the Response body inside ErrorDecoder so errors arrive at the caller with no context.
Discuss Feign's per-method configuration via `@FeignClient(configuration = ...)`, custom `Contract` for non-Spring MVC annotations, `RequestInterceptor` ordering, decoded-exception propagation via `ErrorDecoder`, and the Resilience4j bulkhead as a complement to the circuit breaker.
Interview-ready guide
Interview answer
OpenFeign is still the right pick when you're already on Spring Cloud. It turns an HTTP call into an interface method, and then four pillars make it production-grade.
Declaration and discovery
@FeignClient(name = "payment-service", configuration = PaymentClientConfig.class)— nourl, so Spring Cloud LoadBalancer resolves the host via Eureka, Consul, or Kubernetes- The per-client configuration class scopes interceptors, error decoders, and timeouts to this one remote
RequestInterceptor
- Runs before every call; I inject the bearer token, tenant ID, and correlation headers here
- Keep it scoped to the configuration class so internal tokens never leak to unrelated clients
ErrorDecoder
- The default throws a generic
FeignException— I always replace it with a decoder that maps 400/401/404/409/429/5xx to typed domain exceptions - For 5xx and I/O errors I return
feign.RetryableExceptionso the Retryer fires; everything else short-circuits
Timeouts
- Feign defaults to 10s connect and 60s read, which is how one slow downstream saturates the caller's Tomcat pool
- I set
connectTimeout: 1000andreadTimeout: 2500for internal calls, then compute the retry budget to fit inside the caller's own SLA
Circuit breaker + retry
- I turn on
spring.cloud.openfeign.circuitbreaker.enabled=trueand define a Resilience4jCircuitBreakerper client name - Either Feign's Retryer or Resilience4j
@Retry— never both, or attempts multiply - Fallback bean on
@FeignClient(fallback = ...)for graceful degradation when the circuit opens
Observability
- Micrometer auto-binds
http.client.requestsby client name; Micrometer Tracing injectstraceparentinto every call loggerLevel: basicin production; neverFULL, because it logs bodies and auth headers
When I wouldn't use Feign
- Green-field Spring 6 service with no Spring Cloud —
@HttpExchangeis lighter - Reactive or streaming pipelines — use
WebClientdirectly
OpenFeign + Spring Cloud — Production Integration
Direct Answer
OpenFeign is declarative; you define a Java interface annotated with **@FeignClient**(name, url) and Spring Cloud generates an HTTP proxy. Production-grade usage requires four pillars: RequestInterceptor to inject auth/tracing headers, ErrorDecoder to translate upstream errors into typed exceptions, Resilience4j CircuitBreaker/Retry via @CircuitBreaker on the Feign bean (or Spring Cloud CircuitBreaker auto-config), and explicit Feign.Builder timeouts (connectTimeout, readTimeout) because the defaults are 10s/60s — far too long. Use Spring Cloud LoadBalancer (name = "payment-service" with no url) to resolve service names via Eureka/Consul/Kubernetes.
Code Example
// Enable Feign scanning at app startup
@SpringBootApplication
@EnableFeignClients(basePackages = "com.acme.clients")
public class Application { }
// Declarative client — no url => resolved via LoadBalancer/discovery
@FeignClient(
name = "payment-service",
configuration = PaymentClientConfig.class,
fallback = PaymentClientFallback.class // optional, used when CB opens
)
public interface PaymentClient {
@GetMapping("/api/v1/charges/{id}")
ChargeResponse getCharge(@PathVariable("id") String id);
@PostMapping("/api/v1/charges")
ChargeResponse createCharge(
@RequestHeader("Idempotency-Key") String idemKey,
@RequestBody ChargeRequest body
);
@GetMapping("/api/v1/charges")
Page<ChargeResponse> listCharges(@SpringQueryMap ChargeFilter filter);
}
// Fallback used when the circuit breaker is open (Resilience4j)
@Component
class PaymentClientFallback implements PaymentClient {
@Override public ChargeResponse getCharge(String id) {
return ChargeResponse.unknown(id);
}
@Override public ChargeResponse createCharge(String k, ChargeRequest b) {
throw new PaymentUnavailableException("circuit open");
}
@Override public Page<ChargeResponse> listCharges(ChargeFilter f) {
return Page.empty();
}
}
Common Mistakes & Pitfalls
Shipping @FeignClient with default 10s/60s timeouts and no circuit breaker, so one slow downstream saturates the caller's Tomcat thread pool within minutes. Also: swallowing the Response body inside ErrorDecoder so errors arrive at the caller with no context.
Check your understanding
Answer these without looking back at the article.
- How do you integrate Feign with Resilience4j so retry, circuit breaker, and timeout compose correctly?
- What goes wrong when you leave the default Feign timeouts (10s/60s) in production?
- How does Spring Cloud LoadBalancer resolve a @FeignClient with `name = "payment-service"` and no `url`?
- When would you pick @HttpExchange over @FeignClient in a Spring 6 codebase?
Related interview prep categories
1Want a guided curriculum order?
These modules are also sequenced in our flagship backend interview track — open it when you prefer an ordered path instead of browsing by topic.