Understanding Feign: How Spring Cloud Netflix Implements Declarative HTTP Clients, Hystrix Integration, and Client‑Side Load Balancing
This article explains the purpose and inner workings of Feign in Spring Cloud Netflix, covering how it creates dynamic proxy objects for declarative HTTP calls, integrates with Hystrix for circuit‑breaker support, and works with Ribbon for client‑side load balancing within microservice architectures.
Background
In recent years, microservice governance has become a major focus, and Spring Cloud Netflix has become popular because Netflix successfully implemented many solutions. This article introduces the Feign framework used for inter‑service calls in Spring Cloud Netflix, explaining what Feign is and how it works.
Feign is a declarative, template‑based HTTP client that makes remote HTTP calls look like local method invocations, hiding the underlying HTTP request logic. By generating a dynamic proxy for a target service interface, Feign encapsulates HTTP request logic into Java code, simplifying inter‑service communication.
The article will detail how Feign creates proxy objects, the full execution flow of proxy methods, and how Feign collaborates with Hystrix and Eureka.
Feign
First, an interface is needed to define the methods of the target service. The example uses the official Feign documentation to call GitHub services:
<code style="margin: 0 2px; font-size: 0.92rem; background-color: #f8f8f8; font-family: Source Sans Pro, Roboto Mono, Monaco, courier, monospace"><span><span style="color: #a626a4">interface</span> <span style="color: #c18401">GitHub</span> {</span>
<span style="color: #4078f2">@RequestLine</span>(<span style="color: #50a14f">"GET /repos/{owner}/{repo}/contributors"</span>)
List<Contributor> contributors(<span style="color: #4078f2">@Param</span>(<span style="color: #50a14f">"owner"</span>) String owner, <span style="color: #4078f2">@Param</span>(<span style="color: #50a14f">"repo"</span>) String repo);
<span style="color: #4078f2">@RequestLine</span>(<span style="color: #50a14f">"POST /repos/{owner}/{repo}/issues"</span>)
<span style="color: #a626a4">void</span> createIssue(Issue issue, <span style="color: #4078f2">@Param</span>(<span style="color: #50a14f">"owner"</span>) String owner, <span style="color: #4078f2">@Param</span>(<span style="color: #50a14f">"repo"</span>) String repo);
}
</code>Creation Process
Using the interface above, Feign builds a proxy object for GitHub:
<code style="margin: 0 2px; font-size: 0.92rem; background-color: #f8f8f8; font-family: Source Sans Pro, Roboto Mono, Monaco, courier, monospace"><span style="color: #a626a4">public</span> <span><span style="color: #a626a4">class</span> <span style="color: #c18401">MyApp</span> {</span>
<span><span style="color: #a626a4">public</span> <span style="color: #a626a4">static</span> <span style="color: #a626a4">void</span> <span style="color: #4078f2">main</span><span>(String... args)</span> </span>{
GitHub github = Feign.builder()
.decoder(<span style="color: #a626a4">new</span> GsonDecoder())
.target(GitHub.class, <span style="color: #50a14f">"https://api.github.com"</span>);
}
}
</code>Feign uses a Builder pattern; the build() method creates a ReflectiveFeign implementation. The builder returns a proxy via ReflectiveFeign.newInstance(Target<T> target), which internally uses Java dynamic proxies.
<code style="margin: 0 2px; font-size: 0.92rem; background-color: #f8f8f8; font-family: Source Sans Pro, Roboto Mono, Monaco, courier, monospace"><span style="color: #a626a4">public</span> <T> T target(Target<T> target) {
return build().newInstance(target);
}
</code>The generated proxy implements InvocationHandler. The core handler is FeignInvocationHandler:
<code style="margin: 0 2px; font-size: 0.92rem; background-color: #f8f8f8; font-family: Source Sans Pro, Roboto Mono, Monaco, courier, monospace"><span style="color: #a626a4">static</span> <span><span style="color: #a626a4">class</span> <span style="color: #c18401">FeignInvocationHandler</span> <span style="color: #a626a4">implements</span> <span style="color: #c18401">InvocationHandler</span> </span>{
<span style="color: #a626a4">private</span> <span style="color: #a626a4">final</span> Target target;
<span style="color: #a626a4">private</span> <span style="color: #a626a4">final</span> Map<Method, MethodHandler> dispatch;
<span style="color: #4078f2">@Override</span>
<span><span style="color: #a626a4">public</span> Object <span style="color: #4078f2">invoke</span>(Object proxy, Method method, Object[] args) <span style="color: #a626a4">throws</span> Throwable </span>{
return dispatch.get(method).invoke(args);
}
}
</code>FeignInvocationHandler
The handler stores a map from method to MethodHandler. When a method is invoked, the corresponding MethodHandler executes the HTTP request.
SynchronousMethodHandler
In the default builder, the MethodHandler implementation is synchronous:
<code style="margin: 0 2px; font-size: 0.92rem; background-color: #f8f8f8; font-family: Source Sans Pro, Roboto Mono, Monaco, courier, monospace"><span style="color: #a626a4">public</span> Object <span style="color: #4078f2">invoke</span>(Object[] argv) <span style="color: #a626a4">throws</span> Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
return executeAndDecode(template, options);
}
</code>The handler builds a RequestTemplate from method arguments and then delegates to the underlying Client to perform the HTTP call.
ParseHandlersByName
This class parses the interface metadata and creates a MethodHandler for each method:
<code style="margin: 0 2px; font-size: 0.92rem; background-color: #f8f8f8; font-family: Source Sans Pro, Roboto Mono, Monaco, courier, monospace">public Map<String, MethodHandler> apply(Target target) {
List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
for (MethodMetadata md : metadata) {
result.put(md.configKey(),
factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
}
return result;
}
</code>Contract
The Contract interface defines how annotations on the interface are interpreted to produce MethodMetadata, which later drives the creation of RequestTemplate objects. Feign’s default contract supports annotations such as @RequestLine, @Param, @Headers, @QueryMap, @HeaderMap, and @Body. It can also be extended to support JAX‑RS, SOAP, etc.
Execution Process
After the proxy is created, a method call follows these steps:
Invoke the method on the proxy, which triggers FeignInvocationHandler.invoke and retrieves the appropriate SynchronousMethodHandler.
The handler builds a RequestTemplate from the method arguments.
The Client (e.g., OkHttpClient, RibbonClient) sends the HTTP request and returns the response.
HystrixFeign
Feign can be combined with Hystrix to add circuit‑breaker protection. Hystrix wraps each method call in a HystrixCommand:
<code style="margin: 0 2px; font-size: 0.92rem; background-color: #f8f8f8; font-family: Source Sans Pro, Roboto Mono, Monaco, courier, monospace"><span style="color: #a626a4">public</span> <span><span style="color: #a626a4">class</span> <span style="color: #c18401">MyApp</span> {</span>
<span><span style="color: #a626a4">public</span> <span style="color: #a626a4">static</span> <span style="color: #a626a4">void</span> <span style="color: #4078f2">main</span><span>(String... args)</span> </span>{
GitHub github = HystrixFeign.builder()
.decoder(<span style="color: #a626a4">new</span> GsonDecoder())
.target(GitHub.class, <span style="color: #50a14f">"https://api.github.com"</span>);
}
}
</code>The builder replaces the default InvocationHandlerFactory with one that creates HystrixInvocationHandler and wraps the original contract with HystrixDelegatingContract to handle return types such as HystrixCommand, Observable, etc.
<code style="margin: 0 2px; font-size: 0.92rem; background-color: #f8f8f8; font-family: Source Sans Pro, Roboto Mono, Monaco, courier, monospace"><span style="color: #a626a4">public</span> <span style="color: #c18401">class</span> <span style="color: #c18401">HystrixFeign</span> {
<span style="color: #a626a4">public</span> Builder builder() { ... }
<span style="color: #a626a4">public</span> <span style="color: #c18401">Builder</span> Builder() { ... }
}
</code>HystrixInvocationHandler
<code style="margin: 0 2px; font-size: 0.92rem; background-color: #f8f8f8; font-family: Source Sans Pro, Roboto Mono, Monaco, courier, monospace"><span style="color: #a626a4">public</span> Object <span style="color: #4078f2">invoke</span>(final Object proxy, final Method method, final Object[] args) {
HystrixCommand<Object> hystrixCommand = new HystrixCommand<Object>(setterMethodMap.get(method)) {
@Override protected Object run() throws Exception {
return dispatch.get(method).invoke(args);
}
@Override protected Object getFallback() { ... }
};
if (Util.isDefault(method)) {
return hystrixCommand.execute();
} else if (isReturnsHystrixCommand(method)) {
return hystrixCommand;
} else {
return hystrixCommand.execute();
}
}
</code>Client‑Side Load Balancing
Feign’s low‑level HTTP request is delegated to a Client implementation. Using RibbonClient enables simple client‑side load balancing:
<code style="margin: 0 2px; font-size: 0.92rem; background-color: #f8f8f8; font-family: Source Sans Pro, Roboto Mono, Monaco, courier, monospace"><span style="color: #a626a4">public</span> <span><span style="color: #a626a4">class</span> <span style="color: #c18401">MyApp</span> {</span>
<span><span style="color: #a626a4">public</span> <span style="color: #a626a4">static</span> <span style="color: #a626a4">void</span> <span style="color: #4078f2">main</span><span>(String... args)</span> </span>{
ServiceA serviceA = Feign.builder()
.client(RibbonClient.builder().build())
.decoder(<span style="color: #a626a4">new</span> GsonDecoder())
.target(ServiceA.class, <span style="color: #50a14f">"http://service-a"</span>);
}
}
</code>The RibbonClient extracts the host part (e.g., service-a) as the client name, looks up a load‑balancer instance from LBClientFactory, and forwards the request to a chosen server.
<code style="margin: 0 2px; font-size: 0.92rem; background-color: #f8f8f8; font-family: Source Sans Pro, Roboto Mono, Monaco, courier, monospace"><span style="color: #a626a4">public</span> Response <span style="color: #4078f2">execute</span>(Request request, Request.Options options) throws IOException {
URI asUri = URI.create(request.url());
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
LBClient.RibbonRequest ribbonRequest = new LBClient.RibbonRequest(delegate, request, uriWithoutHost);
return lbClient(clientName).executeWithLoadBalancer(ribbonRequest, new FeignOptionsClientConfig(options)).toResponse();
}
</code>The underlying LBClient uses an ILoadBalancer (e.g., ZoneAwareLoadBalancer) to select a server from a list, which can be populated from Eureka via DiscoveryEnabledNIWSServerList.
Summary
This article described Feign’s purpose, its internal proxy creation and request handling mechanisms, and how it integrates with Hystrix for circuit breaking and with Ribbon for client‑side load balancing, providing practical guidance for developers using the Spring Cloud Netflix stack.
Links
[1]Dynamic Proxy: https://www.liaoxuefeng.com/wiki/1252599548343744/1264804593397984 [2] Ribbon: https://github.com/Netflix/ribbon
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
