Cloud Native 10 min read

How Does Feign Work? A Deep Dive into Spring Cloud’s HTTP Client

This article explains Feign’s internal workflow, annotation attributes, GZIP compression, logging, timeout configuration, and multi‑parameter handling in Spring Cloud, providing code examples and practical tips for building resilient microservice clients.

Java Interview Crash Guide
Java Interview Crash Guide
Java Interview Crash Guide
How Does Feign Work? A Deep Dive into Spring Cloud’s HTTP Client

Feign Working Principle

The main program entry adds the @EnableFeignClients annotation to enable scanning and loading of @FeignClient interfaces. During startup, Spring scans all classes annotated with @FeignClient, registers them in the IOC container, and creates a JDK proxy for each method, which builds a RequestTemplate containing all HTTP request details.

When the proxy is generated, Feign creates a RequestTemplate for each interface method, encapsulating HTTP method, URL, headers, and parameters. The template then produces a Request that is handed to a Client implementation such as JDK URLConnection, Apache HttpClient, or OKHttp. Finally, the client is wrapped by LoadBalanceClient, which integrates Ribbon for load‑balanced service calls.

Feign Annotation Analysis

name: specifies the Feign client name, used as the service name for Ribbon discovery.

url: used for debugging to set a fixed address.

decode404: if true, decodes 404 responses instead of throwing an exception.

configuration: custom configuration class for encoder, decoder, log level, contract, etc.

fallback: class that implements the interface to provide fallback logic on failure.

fallbackFactory: factory to create fallback instances, reducing duplicate code.

path: defines a common prefix for all requests of the client.

Feign GZIP Compression

Spring Cloud Feign supports GZIP compression for both requests and responses to improve efficiency.

feign:
  compression:
    request:
      enabled: true   # enable request compression
      mimeTypes:      # e.g., text/xml,application/xml,application/json
      minRequestSize: 2048  # minimum size to trigger compression
    response:
      enabled: true   # enable response compression

When compression is enabled, responses must be received as binary data using ResponseEntity<byte[]>.

@FeignClient(name = "github-client", url = "https://api.github.com", configuration = HelloFeignServiceConfig.class)
public interface HelloFeignService {

    /*
     * If compression is used, the return type must be binary.
     */
    @RequestMapping(value = "/search/repositories", method = RequestMethod.GET)
    ResponseEntity<byte[]> searchRepositories(@RequestParam("q") String parameter);
}

Feign Client Logging

To enable logging, provide a configuration class that defines a Logger.Level bean.

@Configuration
public class HelloFeignServiceConfig {

    /**
     * Logger levels:
     * NONE – no logging
     * BASIC – method, URL, status, time
     * HEADERS – BASIC plus request/response headers
     * FULL – all details including body and metadata
     */
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

Feign Timeout Settings

Feign calls involve two layers: Ribbon for load balancing and Hystrix for circuit breaking. Timeouts can be configured as follows:

If you encounter the above error, add the following properties:

# Request processing timeout
ribbon.ReadTimeout: 12000
# Connection timeout
ribbon.ConnectionTimeout: 30000

When Hystrix is enabled, additional configuration is needed:

hystrix:
  command:
    default:
      circuitBreaker:
        sleepWindowInMilliseconds: 30000
        requestVolumeThreshold: 50
      execution:
        timeout:
          enabled: true
        isolation:
          strategy: SEMAPHORE
          semaphore:
            maxConcurrentRequests: 50
          thread:
            timeoutInMilliseconds: 100000

Feign POST and GET Multi‑Parameter Transfer

Spring MVC can bind request parameters to POJOs, but Feign does not fully support this for GET requests. Work‑arounds include:

Write each POJO field as a separate method parameter.

Convert parameters into a Map.

Use @RequestBody with a POJO (violates pure REST principles).

A request interceptor can transform a GET request with a JSON body into query parameters:

@Component
public class FeignRequestInterceptor implements RequestInterceptor {
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void apply(RequestTemplate requestTemplate) {
        // Feign does not support GET with POJO; convert JSON body to query map
        if (requestTemplate.method().equalsIgnoreCase("GET") && requestTemplate.body() != null) {
            try {
                JsonNode jsonNode = objectMapper.readTree(requestTemplate.body());
                requestTemplate.body(null);
                Map<String, Collection<String>> queries = new HashMap<>();
                buildQuery(jsonNode, "", queries);
                requestTemplate.queries(queries);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void buildQuery(JsonNode jsonNode, String path, Map<String, Collection<String>> queries) {
        if (!jsonNode.isContainerNode()) { // leaf node
            if (jsonNode.isNull()) {
                return;
            }
            Collection<String> values = queries.get(path);
            if (values == null) {
                values = new ArrayList<>();
                queries.put(path, values);
            }
            values.add(jsonNode.asText());
            return;
        }
        if (jsonNode.isArray()) { // array node
            Iterator<JsonNode> it = jsonNode.elements();
            while (it.hasNext()) {
                buildQuery(it.next(), path, queries);
            }
        } else { // object node
            Iterator<Map.Entry<String, JsonNode>> it = jsonNode.fields();
            while (it.hasNext()) {
                Map.Entry<String, JsonNode> entry = it.next();
                if (StringUtils.hasText(path)) {
                    buildQuery(entry.getValue(), path + "." + entry.getKey(), queries);
                } else {
                    // root node
                    buildQuery(entry.getValue(), entry.getKey(), queries);
                }
            }
        }
    }
}

Alternatively, the venus-cloud-feign dependency provides built‑in GET parameter mapping:

<!-- https://mvnrepository.com/artifact/cn.springcloud.feign/venus-cloud-feign-core -->
<dependency>
    <groupId>cn.springcloud.feign</groupId>
    <artifactId>venus-cloud-feign-core</artifactId>
    <version>1.0.0</version>
</dependency>

GitHub repository:

https://github.com/SpringCloud/venus-cloud-feign.git
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javaload balancingfeignSpring CloudHTTP client
Java Interview Crash Guide
Written by

Java Interview Crash Guide

Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.