Spring Cloud quality project, Feign's use extension

1 Preface

SpringCloud is a development framework based on Restful. In the overall call process, even if Eureka is introduced, the consumer needs to use a complete path to normally access the remote interface. At the same time, the developer also needs to manually use RestTemplate to convert the call and return results. In order to solve this complex call logic, Feign Technology (relying on Ribbon technology support) is provided in spring cloud. Using this technology, remote Restful services can be mapped to remote interfaces, and the consumer can realize remote method calls through remote interfaces.

1.1 what is Feign

Feign means "pretend, disguise and deform" in English. It is a lightweight framework for HTTP request invocation. It can invoke HTTP requests in the way of java interface annotation instead of directly invoking HTTP request messages in Java. Through processing annotations, the request is templated. When the actual call is made, the parameters are passed in and then applied to the request according to the parameters, and then transformed into a real request. This request is relatively intuitive.

1.2 what is OpenFeign

OpenFeign is a declarative call. We only need to describe our interface according to certain rules. It can help us complete REST style calls, greatly reduce the amount of code and improve the readability of the code. Note that the OpenFeign mentioned here is a third-party component. In order to reduce the learning cost of developers, the rules given by Spring Cloud after encapsulating OpenFeign completely adopt the style of Spring MVC, that is, as long as developers are familiar with Spring MVC, they can easily complete the description of interfaces and the function of service calls, You don't have to learn OpenFeign's own declaration rules.

1.3 relationship between feign and OpenFeign

Feign itself does not support Spring MVC annotations. It has its own set of annotations.

OpenFeign is a Spring Cloud that supports Spring MVC annotations based on Feign, such as @ RequesMapping.
OpenFeign's @ FeignClient can parse the interface under the @ RequestMapping annotation of spring MVC, generate implementation classes through dynamic proxy, implement load balancing in the classes and call other services.

1.4 Feign working principle

  • Add the @ EnableFeignClients annotation at the main program entry to start the scanning and loading processing of Feign Client. Define the interface and add @ FeignClients annotation according to Feign Client's development specification.
  • When the program starts, it will scan the package, scan all @ FeignClients annotated classes, and inject this information into the Spring IOC container. When the method in the defined Feign interface is called, a specific RequestTemplate is generated through the JDK proxy. When the proxy is generated, Feign will create a RequestTemplate object for each interface method, which encapsulates all the information required by the HTTP request, such as request parameter name, request method and other information, which are determined in this process.
  • Then, the RequestTemplate generates a Request and sends the Request to the Client for processing. The Client here can be JDK native URLConnection, Apache Http Client, or Okhttp. Finally, the Client is encapsulated into the LoadBalanceClient class, which initiates calls between services in combination with Ribbon load balancing.

2 Feign basic functions

2.1 FeignClient notes

FeignClient annotation is modified by @ Target (ElementType.TYPE), indicating that the function Target of FeignClient annotation is on the interface. When you open the org.springframework.cloud.openfeign.FeignClient annotation definition class, you can see the properties corresponding to the FeignClient annotation.
Common attributes of FeignClient annotation are summarized as follows:

  • Name: specify the name of FeignClient. If the project uses Ribbon, the name attribute will be used as the name of the micro service for service discovery.
  • url: url is generally used for debugging. You can manually specify the address of @ FeignClient call.
  • decode404: when a 404 error occurs, if the field is true, the decoder will be called for decoding, otherwise FeignException will be thrown.
  • Configuration: Feign configuration class. You can customize Feign's Encoder, Decoder, LogLevel and Contract.
  • Fallback: defines a fault-tolerant processing class. When calling the remote interface fails or expires, the fault-tolerant logic of the corresponding interface will be called. The class specified by fallback must implement the interface marked @ FeignClient.
  • fallbackFactory: factory class, used to generate fallback class examples. Through this attribute, we can implement the common fault-tolerant logic of each interface and reduce duplicate code.
  • path: defines the uniform prefix of the current FeignClient.

2.2 Feign turns on GZIP compression

Spring Cloud Feign supports GZIP compression of requests and responses to improve communication efficiency. The configuration of enabling GZIP compression by Feign will be described below.

feign:
    compression:      
  request:      
      enabled: true	#Configure request GZIP compression  
      mime-types: text/xml,application/xml,application/json	# Configure MIME types supported by compression      
      min-request-size: 1024  # Configure the lower limit of compressed data size (unit: B)      
  response:      
      enabled: true # Configure response GZIP compression

2.3 Feign startup log

Feign provides an instance of feign.Logger for each feign client. You can start the log in the configuration. The opening method is relatively simple, which is divided into two steps.

Step 1: configure log output in application.yml.

logging:
    level: cn.springcloud.book.feign.service.HelloFeignService: debug

Step 2:
Configure the log Bean through Java code. The code is as follows:

package com.pps.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import feign.Logger;

@Configuration
public class FeiginConfig {

	@Bean
	Logger.Level logLevel(){
		
		return Logger.Level.BASIC;
	}
}

2.4 Feign timeout setting

Feign calls are divided into two layers, that is, Ribbon calls and Hystrix calls. Higher versions of Hystrix are closed by default.

  • If the following error message appears, it indicates that the Ribbon processing has timed out.

    feign.RetryableException: Read timed out executing POST http://******
    at feign.FeignException.errorExecuting(FeignException.java:67) at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMet
    hodHandler.java:104) at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler
    .java:76) at feign.ReflectiveFeign F e i g n I n v o c a t i o n H a n d l e r . i n v o k e ( R e f l e c t i v e F e i g n . j a v a : 103 ) a t c o m . s u n . p r o x y . FeignInvocationHandler.invoke(Reflective Feign.java:103) at com.sun.proxy. FeignInvocationHandler.invoke(ReflectiveFeign.java:103)atcom.sun.proxy.Proxy113.getBaseRow(Unknown Source)Caused by: java.net.SocketTimeoutException: Read timed out

At this time, you can set the configuration information of the Ribbon as follows.

#Timeout for request processing
ribbon.ReadTimeout: 120000#Timeout for requesting a connection
ribbon.ConnectTimeout: 30
  • If you enable Hystrix, the super error message of Hystrix is as follows:

    com.netflix.hystrix.exception.HystrixRuntimeException: FeignDemo#demo() timed-out and no fallback available.
    at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:819) at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:804) at rx.internal.operators.OperatorOnErrorResumeNextViaFunction 4. o n E r r o r ( O p e r a t o r O n E r r o r R e s u m e N e x t V i a F u n c t i o n . j a v a : 140 ) a t r x . i n t e r n a l . o p e r a t o r s . O n S u b s c r i b e D o O n E a c h 4.onError(OperatorOnErrorResumeNextViaFunc tion.java:140) at rx.internal.operators.OnSubscribeDoOnEach 4.onError(OperatorOnErrorResumeNextViaFunction.java:140)atrx.internal.operators.OnSubscribeDoOnEachDoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)

When you see the above error message, it indicates that the Hystrix is reporting an error when it is over. At this time, set the configuration information of the Hystrix as follows:

hystrix: 
	shareSecurityContext: true    
	command:       
    	default:	
    		circuitBreaker: 
    			sleepWindowInMilliseconds: 100000                
    			forceClosed: true            
    		execution:                
    			isolation:                    
    				thread:
                        timeoutInMilliseconds: 600000

3 Feign advanced applications

3.1 replacement of feign default Client

Feign uses the JDK native URLConnection to send HTTP requests by default. There is no connection pool, but a long connection will be maintained for each address, that is, using the HTTP persistence connection. We can replace feign's original HTTP Client with Apache's HTTP Client, and tune the calls between services by setting connection pool, timeout, etc. Spring cloud supports this replacement from Brixtion.SR5. Next, let's introduce how to replace feign's default Client with HTTP Client and okhttp.

  • Replace Feign default Client with HTTP Client

The first step is to introduce Apache HttpClient into pom.xml to replace Spring Cloud OpenFeign

<dependencies>
    <dependency>        
        <groupId>org.springframework.boot</groupId>        
        <artifactId>spring-boot-starter-web</artifactId>    
    </dependency>
    <!-- Spring Cloud OpenFeign of Starter Dependence of --> 
    <dependency>        
        <groupId>org.springframework.cloud</groupId>       
        <artifactId>spring-cloud-starter-openfeign</artifactId>    
        </dependency>    
    <!-- use Apache HttpClient replace Feign Primordial httpclient -->    
    <dependency>        
        <groupId>org.apache.httpcomponents</groupId>        
        <artifactId>httpclient</artifactId>    
    </dependency>    
    <dependency>        
    	<groupId>com.netflix.feign</groupId>       
    	<artifactId>feign-httpclient</artifactId>       
    	<version>8.17.0</version>    
    </dependency>
</dependencies>

Step 2: configure in application.yml to let Feign load HTTP Client to replace the default Client when it starts

server:
    port: 8610
spring: 
	application:       
 		name: pps-httpclient
feign:    
	httpclient:       
 		enabled: true

The third step is to run the main program.

  • Replace Feign's default Client with okhttp

TP is a common network request mode at present. It is used to exchange data for access requests. The effective use of HTTP can make the application access faster and save bandwidth. okhttp is a great HTTP client with the following functions and features.

(1) SPDY is supported, and multiple requests to the same host can be merged.
(2) Use connection pooling technology to reduce the latency of requests (if SPDY is available).
(3) Use GZIP compression to reduce the amount of data transmitted.
(4) Cache responses to avoid duplicate network requests.

Next, let's introduce how to use okhttp to replace Feign's default Client.

The first step is to add dependencies to pom.xml

<dependencies>
    <dependency>        
        <groupId>org.springframework.boot</groupId>        
        <artifactId>spring-boot-starter-web</artifactId>    
    </dependency>    
    <!-- Spring Cloud OpenFeign of Starter Dependence of -->    
    <dependency>        
        <groupId>org.springframework.cloud</groupId>        
        <artifactId>spring-cloud-starter-openfeign</artifactId>    
    </dependency>    
    <dependency>        
        <groupId>io.github.openfeign</groupId>        
        <artifactId>feign-okhttp</artifactId>    
    </dependency>
</dependencies>

Step 2: open okhttp as Feign's default Client

server:
    port: 8611
spring: 
	application:       
 		name: pps-okhttp

feign:   
	httpclient:       
  		enabled: false    
  	okhttp:       
  		enabled: true

Step 3: okhttpclient is the executor of the core functions of okhttp. You can use okhttpclient = new okhttp client(); To create the default okhttpclient object, you can also use the following code to build a custom okhttpclient object. The above configuration information only gives common settings, and other settings are complex. Interested readers can expand their reading and learning.

@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignOkHttpConfig {   
    @Bean   
    public okhttp3.OkHttpClient okHttpClient(){
        return new okhttp3.OkHttpClient.Builder() 
            //Set connection timeout
            .connectTimeout(60, TimeUnit.SECONDS)            
            //Set read timeout            
            .readTimeout(60, TimeUnit.SECONDS)           
            //Set write timeout            
            .writeTimeout(60,TimeUnit.SECONDS)           
            //Auto reconnect            
            .retryOnConnectionFailure(true)            
            .connectionPool(new ConnectionPool())           
            //Build OkHttpClient object            
            .build();    
	}              
}

Step 4: start the main program.

3.2 Feign's Post and Get multiparameter transfer

In the actual project development process, we use Feign to realize the invocation between services. However, in many cases, multi parameter passing is unavoidable. The application method in Feign's RequestInterceptor is implemented to perform unified interception and conversion, and deal with the problem of multi parameter transmission of GET method in Feign.

@Component
public class FeignRequestInterceptor implements RequestInterceptor {    
    @Autowired   
    private ObjectMapper objectMapper;    
    @Override    
    public void apply(RequestTemplate template) {       
        //feign does not support the transfer of GET method to POJO and JSON body to query       
        if ("GET".equals(template.method()) && null != template.body() ) {          
          try {    
              JsonNode jsonNode = objectMapper.readTree(template.body());          
              template.body(null);          
              Map<String, Collection<String>> queries = new HashMap<>();          
              buildQuery(jsonNode, "", queries);          
              template.queries(queries);          
          } catch (IOException e) {     
              e.printStackTrace();          
		}        
	}    
}

3.3 Feign's first request failure

After Feign and Ribbon integrate Hystrix, the first call may fail. The causes of the problem are as follows:
The default timeout of Hystrix is 1 second. If no response is made after this time, it will enter the fallback code. Due to Bean assembly and lazy loading mechanism, Feign's first request will be slow. If the response time is greater than 1 second, the request fails.

Next, take feign as an example to introduce three methods to deal with the failure of feign's first request.

  • Scheme 1: change the timeout of Hystrix to 5 seconds, and the configuration is as follows:

    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000

  • Scheme 2: disable the timeout of Hystrix. The configuration is as follows:

    hystrix.command.default.execution.timeout.enabled: false

3.4 passing Token by feign call

During authentication, whether jwt or security, when Feign is used, it will be found that when an external request is sent to service A, service A can get A Token. However, when the service uses Feign to call service B, the Token will be lost, resulting in authentication failure. The solution is relatively simple. What you need to do is to add the Token to be passed to the request header when Feign is called.

We only need to implement an interface RequestInterceptor provided by Feign. Assuming that the key in the request Header is oauthToken when verifying permissions, we first obtain the Token whose key in the current request is oauthToken, and then put it on Feign's request Header.

package com.pps.common.config;

import feign.RequestInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;

/**
 * @author pangps
 * feign Interceptor
 */
@Configuration
public class FeignInterceptorConfig {
    private static final Logger logger = LoggerFactory.getLogger(FeignInterceptorConfig.class);
    /**
     * When using feign client to access other microservices, access_ Put the token into the parameter or header, Authorization:Bearer xxx
     * Or url?access_token=xxx
     */
    @Bean
    public RequestInterceptor requestInterceptor() {
        return template -> {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null) {
                if (authentication instanceof OAuth2Authentication) {
                    OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
                    String accessToken = details.getTokenValue();
                    template.header("Authorization", OAuth2AccessToken.BEARER_TYPE + " " + accessToken);
                }
            }
            template.header("isInterService", "true");
        };
    }
}

4 yards of insight

4.1 Venus cloud feign recommendation

Venus cloud feign, actual combat enhancement of Spring Cloud Feign.

  • Package name specification

cn.springcloud.feign

  • Maven warehouse

    cn.springcloud.feign venus-cloud-starter-feign 1.0.0
  • Application scenario

This is mainly due to the use of API(SDK) for laziness and a series of problems caused by the version in the Restful API path.

For convenience, Feign is used in the API instead of RestTemplate manual call. When writing Feign interface, if you want to use Spring MVC annotation, you can only write it once in Feign interface, and then implement the interface by implementing the class. However, Spring MVC does not support annotations on method parameters in the implementation interface (annotations on inherited classes and methods are supported). Spring Cloud Chinese community enhanced Spring Cloud openfeign and named it Venus cloud Feign.

  • Version support

The version of Spring Cloud is Finchley.RELEASE.

  • Project open source address

https://github.com/springcloud/venus-cloud-feign

4.2 references

Feign official website: those with a high level of English can directly see the official documents

Feign official website Chinese version: those with poor English level can read the Chinese version first and then the official website

Tags: Java jvm Spring Boot Spring Cloud

Posted on Wed, 17 Nov 2021 01:00:09 -0500 by maxkbr