Banning Feign: A Unified RPC Client That Fixes Context, Timeout, and Fallback by Default
Teams running deep microservice call chains hit Feign's limits when context propagation breaks silently, timeouts stack up, and fallback code multiplies. A single-client approach that bakes in cross-cutting concerns removes whole categories of production incidents and boilerplate.
Feign's declarative interfaces come with hidden costs: context propagation via scattered RequestInterceptors, coarse global timeouts that accumulate across call chains, and per-interface Fallback classes that multiply with every service. MetaLite's InternalServiceClient collapses all of this into four core methods that accept an explicit RpcRequest object specifying the provider, endpoint, and expected response type.
Context headers for TraceId, user ID, AppId, and Seata XID are injected automatically inside a single checkAndFillRpcRequest method. Timeout values decrement by a fixed margin at each hop, so a 5-second call from service A becomes 4.5 seconds at B and 4 seconds at C, capping end-user wait time and encouraging fast failure downstream. An AOP aspect wraps every call with unified logging, exception handling, and monitoring instrumentation.
The trade-off is explicit: callers construct a request object instead of annotating an interface, but they shed the need for separate Feign interfaces, Fallback implementations, and interceptor configuration. Service discovery runs through Nacos, and response types are validated eagerly at the call site rather than silently at runtime.
Feign's declarative style hides the fact that interface definitions and Controller implementations are completely decoupled — path or parameter changes on the server side cause runtime failures with no compile-time feedback.
The timeout-decrement pattern is a simple arithmetic fix to a systemic problem: without it, every team sets generous timeouts defensively, and the sum punishes the user.
Moving from annotation-driven service binding to explicit provider strings in each request trades IDE autocompletion for operational clarity, which matters more when debugging a broken call chain.
InternalServiceClient treats RPC as infrastructure, not as a per-service contract, which aligns with the reality that most internal calls share the same cross-cutting requirements.