Feign: fusing, introduction to @FeignClient annotation parameters, optimization

Compared with RestTemplate, it is easier to understand and the operation is more dynamic. code show as below:

@GetMapping("/buy/{id}")
public Product order() {
      Product product = restTemplate.getForObject("http://shop-serviceproduct/product/1",Product.class);
      return product;
}

As can be seen from the code, we construct the URL by concatenating strings, and the URL has only one parameter. However, in reality, URLs often contain multiple parameters.

Feign Introduction

Related dependencies:


org.springframework.cloud

spring-cloud-starter-openfeign

Feign is a declarative, templated HTTP client developed by Netflix.
In Spring Cloud, using Feign is very simple – create an interface and add the @FeignClient annotation to the interface.
Add the @EnableFeignClients annotation to the consumer’s microservice startup class to enable Spring Cloud Feign support.

package com.zb;

import com.zb.service.impl.TestServiceImpl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.ConfigurableApplicationContext;

/**
* Startup class
*/
@EnableFeignClients
@SpringBootApplication
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class,
    }
}

Feign itself does not directly support the Load Balancing strategy, but implements load balancing by integrating Ribbon. Therefore, Feign can use various load balancing strategies supported by Ribbon, including polling, random, weight, best available, etc.

Service interruption

SpringCloud Fegin has integrated hystrix for Feign by default, so there is no need to add hystrix after adding Feign dependencies.
yml configuration turns on circuit breaker

feign:
  hystrix:
    enabled: true
package com.zb.client;

import com.zb.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
* feign interface
*/
@FeignClient(value = "user-server",fallback = UserFeignClientImpl .class)
public interface UserFeignClient {

    @GetMapping("/user/all")
    String all(@RequestParam("name")User user);

}
package com.zb.client.impl;

import com.zb.client.UserFeignClient;
import com.zb.entity.User;
import org.springframework.stereotype.Component;

/**
* Fusing method
*/
@Component
public class UserFeignClientImpl implements UserFeignClient {

    @Override
    public String all(User user) {
        return "Exception!";
    }
}

@FeignClient annotation

Parameters in annotations

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.cloud.openfeign;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
    @AliasFor("name")
    String value() default "";

    /** @deprecated */
    @Deprecated
    String serviceId() default "";

    String contextId() default "";

    @AliasFor("value")
    String name() default "";

    String qualifier() default "";

    String url() default "";

    boolean decode404() default false;

    Class<?>[] configuration() default {};

    Class<?> fallback() default void.class;

    Class<?> fallbackFactory() default void.class;

    String path() default "";

    boolean primary() default true;
}

Parameter introduction

  1. Both value and name can specify the provider service name. @FeignClient(“user-server”) specifies the service name by default.
    @FeignClient(value = "user")
    
    @FeignClient(name = "user")
    
    @FeignClient("user")
  2. fallback and fallbackFactory
    fallback defines a fault-tolerant processing class. When calling the remote interface fails or times out, the corresponding fault-tolerant method will be called.
    fallbackFactory can capture all exceptions that occur in the Feign interface, and can also implement fallback-related interfaces for custom rollback code or logging, etc. It can also reduce repeated code
    @FeignClient(value = 'user', fallback = UserFeignClientImpl.class)
    
    @FeignClient(value = 'user', fallbackFactory = UserFallbackFactory.class)
  3. The path parameter does not affect the use of the Feign interface at all.

    @FeignClient(value = 'user',path = '/user')
    

    Define the unified prefix of the current FeignClient

  4. primary default value is true. This class can be converted into a Bean

  5. url: For debugging, you can manually specify the address called by @FeignClient
    @FeignClient(value = 'user',url = "http://localhost:9090/pay/create")
    
  6. decode404: Default is false. When an http 404 error occurs, if this field is true, the decoder will be called to decode, otherwise a FeignException will be thrown.
    @FeignClient(value = "user-server",decode404 = true)
  7. configuration: Feign configuration class, you can customize Feign’s Encoder, Decoder, LogLevel, ContractcontextId: you can set aliases for the feign interface with the same service name to solve the problem of the same name
    // The first feign interface
    @FeignClient(value = "user",contextId = "aa")
    //The second feign interface
    @FeignClient(value = "user",contextId = "bb")

Feign performance optimization

mainly include:

  1. Use http requests with connection pooling instead of the default URLConnection
  2. Log level, use the one that prints the least (basic or none)

1. Usage logs and timeout issues

Feign itself has integrated Ribbon dependencies and automatic configuration, so we do not need to introduce additional dependencies or register RestTemplate objects. The bottom layer of load balancing uses Ribbon, so the request timeout configuration here is actually configuring Ribbon. When a request timeout occurs, the following error will appear.

feign:
  client:
    config:
      default: #Specify the name corresponding to feignclients. If default is specified, it means the timeout setting of all clients globally.
        connectTimeout: 10000 #Connection timeout time
        readTimeout: 10000 #Read timeout
        loggerLevel: basic #Set logging level

spring:
  main:
    #Declare whether the spring framework allows the definition of bean objects with duplicate names to overwrite the original beans (default is false)
    allow-bean-definition-overriding: true
  1. FULL: Record basic information, request and response header information, request and response body information
  2. NONE: No logging
  3. BASIC: Record request method, URL, response status code and execution time
  4. HEADERS: Record basic information and request and response header information

Note: When the log configuration is enabled, no error will be reported for the feign timeout issue.

2. Use connection pool

HTTP connections require three handshakes and four waves, which is very performance-intensive; so the HTTP connection pool helps us save this step.
At the same time, Feign’s HTTP client supports three frameworks:
HttpURLConnection, HttpClient, OkHttp; the default is HttpURLConnection

  1. Introduce Apache’s HttpClient dependency
    <dependency> in pom.xml
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-httpclient</artifactId>
    </dependency>
    
  2. Configure connection pool
    Add the following configuration in the yml file: (the connection pool is enabled by default after it is introduced, and it does not need to be written if necessary)
    feign:
      client:
        config:
          default:
            loggerLevel: BASIC
      httpclient:
        enabled: true #Enable feign's support for httpClient
        max-connections: 200 #Maximum number of connections
        max-connections-per-route: 50 #Maximum number of connections per path
    

3.gzip compression

Gzip is the abbreviation of GNUzip, which was first used for file compression in UNIX systems. The gzip compression ratio is about 3 to 10 times, which can greatly save the server’s network bandwidth. In actual applications, not all files are compressed, but only static files are usually compressed.

server:
  compression:
    enabled: true #Enable compression

Gzip decompression code implementation

1. Run the virtual machine
2. Package the code in Maven of Idea
3. Drag the packaged file into the virtual machine
4. Execute jar package

Communication between client and server supports gzip images

FAQ

Feign’s maven dependency reports red, or the @EnableFeignClients on the main startup class is not recognized and keeps reporting red, or the feign interface is injected into the controller and reports red.
Possible causes and solutions:
1. The maven package is not successfully imported, or the maven package download is incomplete. You can clean and compile to delete the dependent packages and then download and import again.
2. If there is a version conflict, you can specify the version number when importing the maven package and try other available versions.

Interview questions

1.What is the difference between Feign and RestTemplate?
Both Feign and RestTemplate are used to make HTTP requests, but they have some differences:
Feign:
Feign is a declarative HTTP client that defines service calls through interfaces and annotations.
Feign automatically generates HTTP request code, and developers only need to define interfaces and methods.
Integrated Ribbon and Hystrix to support load balancing and service degradation.
It is more declarative and concise, suitable for service calls in microservice architecture.
RestTemplate:
RestTemplate is a classic HTTP client that requires manual writing of HTTP request code.
Developers need to construct HTTP requests, handle requests and responses, handle exceptions, etc.
Can be used to call any HTTP service, not limited to microservice architecture.
More flexible and suitable for scenarios that require more control.
Choosing to use Feign or RestTemplate depends on the needs of the project and the preferences of the development team. Feign is often preferred in microservice architectures because it provides a higher level of abstraction and cleaner code.

2. How to pass Header parameters when using Feign?

// 1. Pass @RequestHeader(name = "name")
@FeignClient(name = "xxx-service-name")
public interface XxxFeignClient {
 
    @RequestMapping(value = "/user/info")
    String userTicket(@RequestHeader(name = "token") String token);
}

The obvious disadvantage of this method is that if multiple Header parameters are passed, they need to be added when the interface method is defined, which is more cumbersome.
The second way is to implement the RequestInterceptor interface by defining a configuration class, and then add this configuration class to the @FeignClient annotation. The code example is as follows:

@Configuration
public class HeaderConfiguration implements RequestInterceptor {
    
    @Override
    public void apply(RequestTemplate requestTemplate) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
 
        Enumeration headerNames = request.getHeaderNames();
 
        if (headerNames != null) {
            // Loop through all headers and put the contents of the headers into requestTemplate
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                String values = request.getHeader(name);
                requestTemplate.header(name, values);
            }
        }
    }
}
// Then configure the custom configuration class into Feign to realize the transfer of interface Header parameters in Feign
@FeignClient(value = "user-server", configuration = HeaderConfiguration.class)
public interface UserFeignClient {
 
    @RequestMapping(value = "/user/info")
    String userInfo();
}