This is the 18th day of my participation in the August Challenge
Feign Introduction
What is Feign? A declarative restful HTTP client based on annotations and dynamic proxies.
The principle of
Implementation principle of Feign sending request
-
The @enableFeignClients annotation is marked on the microservice startup class, and then the @FeignClient annotation is marked on the Feign interface. The @feignClient annotation has several parameters that need to be configured, which I won’t repeat here, but are quite simple.
-
The Feign framework scans annotations, processes them through Feign classes, and eventually generates a Feign object.
Parse the @FeignClient annotation to generate a MethodHandler
The specific parse class is ParseHandlerByName. This class is the inner class of ReflectiveFeign.
// Parse annotation metadata, using Contract parsing
List<MethodMetadata> metadata = this.contract.parseAndValidateMetadata(key.type());
Copy the code
Once you get the annotation metadata, you loop through the annotation metadata, creating a MethodHandler for each method that will eventually be called by the proxy object. Eventually the MethodHandler will be stored in the following collection and returned.
Map<String, MethodHandler> result = new LinkedHashMap();
Copy the code
Parse is complete, call ReflectiveFeign. NewInstance () to generate the proxy class.
MethodHandler is an interface of Feign whose invoke method is the method that the invoke() method of the dynamic proxy caller InvocationHandler ultimately calls.
To restate this: The invoke() method of InvocationHandler ends up calling back to MethodHandler’s invoke() to send an HTTP request. This is the concrete implementation of Feign dynamic proxy.
Line 57 of the newInstance() method of the ReflectiveFeign class:
// Create a dynamic proxy caller
InvocationHandler handler = this.factory.create(target, methodToHandler);
// Reflection generates feign interface proxiesT proxy = proxy. newProxyInstance(loader, interface array, handler);Copy the code
InvocationHandler. Invoke () implementation in FeignInvocationHandler. Invoke (), FeignInvocationHandler ReflectiveFeign an inner class. There are a lot of details that I don’t want to talk about here, but let’s go straight to the core line of code, so as not to affect the idea, we are based on Feign implementation principle! Never mind the details!
// The invoke() method of InvocationHandler finally calls back to MethodHandler’s invoke() to send an HTTP request
The invoke() method of the ReflectiveFeign class, line 323, at the end of the code, reads as follows:
(MethodHandler)this.dispatch.get(method). invoke(args);
Copy the code
-
This. dispatch: This is a map that holds all of the methodHandlers. Refer to line 57 of the newInstance() method of the ReflectiveFeign class for where InvocationHandler is created.
-
This.dispatch.get (method) : This method is the name of the method defined in the Feign interface by our developers! All this code is saying is get the method we need to call from the MethodHandler collection.
-
This.dispatch.get (method).invoke(args) : invoke is called methodHander.invoke ()! Dynamic proxy callback proxy class, and that’s it. Oh my God!
MethodHandler. Invoke () implementation: SynchronousMethodHandler. Invoke ()
At this point, this is the logic to send the request. Before sending a request, you first create a request template, and then call RequestInterceptor for request processing.
/ / create the RequestTemplate
RequestTemplate template = this.buildTemlpateFromArgs.create(argv);
// Create feign retries to retry failures
Retryer retryer = this.retryer.clone();
while(true) {try{
// Send the request
return this.executeAndDecode(template);
} catch(RetryableException var5) {
// Retry for a maximum of five timesretryer.continueOrPropagate(); }}Copy the code
The RequestTemplate processing
The RequestTemplate template is processed by a number of interceptors, including the following:
-
BasicAuthRequestInterceptor: authorized interceptors, mainly is to set up the Authorization request header information, here is the base64 code after the user name and password.
-
FeignAcceptGzipEncodingInterceptor: Encoding type interceptor, mainly is to set up the Accept request header – Encoding information, the default value {gzip, deflate}.
-
FeignContextGzipEncodingInterceptor: The interceptor determines whether the value of the context-Length attribute in the request header is greater than the maximum Length of the request content. If it is greater than the maximum Length of 2048, the context-Encoding message is set to {gzip, deflate}. Note that 2048 here is configurable and can be configured in a configuration file:
feign.compression.request.enabled=true
feign.compression.request.min-request-size=2048
Copy the code
Min – request – the size is resolved by FeignClientEncodingProperties, the default value is 2048.
We can also customize the request interceptor, and our custom interceptor will be called at this point, and all classes that implement the RequestTemplate interface will be called here. For example, we can customize the interceptor to put the global transaction ID in the request header.
Use feign.request to wrap the RequestTemplate as feign.request
Feign. Request consists of five parts:
-
method
-
url
-
headers
-
body
-
charset
HTTP request client
Feign supports the following TYPES of HTTP clients:
-
JDK built-in HttpUrlConnection
-
Apache HttpClient
-
OkHttpClient
Default and LoadBalancerFeignClient
response = this.client.execute(request, this.options); The Client interface defines the interface to execute() and implements client.execute () through the interface inner class. HttpURLConnection connection =this.convertAndSend(request, options);
return this.convertResponse(connection).toBuilder(). request(request).build();
Copy the code
-
Options defines two parameters:
-
ConnectTimeoutMillis: indicates the connection timeout period. The default value is 10 seconds.
-
ReadTimeoutMillis: timeout period for reading data. The default value is 60 seconds.
-
This is the simplest implementation, but does not support load balancing. Spring Cloud integrates Feign with the Ribbon, so it is natural to use Feign and the Ribbon together. In other words, before Feign sends the request, it is wrapped as RibbonRequest.
This is another implementation of sending requests LoadBalancerFeignClient.
// Wrap Request as RibbonRequest
RibbonRequest ribbonRequest = new (this.delegate, request, uriWithoutHost);
// Configure the timeout period
IClientConfig requestConfig = this.getClientConfig(options, clientName);
// Send requests in load balancing mode
return ((RibbonResponse)this.IbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig)).toResponse();
Copy the code
Send requests in load balancing mode
-
Enclosing IbClient (clientName). ExecuteWithLoadBalancer (ribbonRequest requestConfig)) implementation in AbstractLoadBalancerAwareClient class.
-
ExecuteWithLoaderBalancer () method also refer to the response of programming, through LoadBalancerCommand submit a request, and then use the observables receiving the response information.
AbstractLoadBalancerAwareClient class executeWithLoadBalancer line 54 () method:
Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
AbstractLoadBalancerAwareClient implements IClient interface, this interface defines the execute () method,
-
AbstractLoadBalancerAwareClient. Enclosing the execute () there are many kinds of concrete implementation:
-
OkHttpLoadBalancingClient
-
RetryableOkHttpLoadBalancingClient
-
RibbonLoadBalancingHttpClient
-
RetryableRibbonLoadBalancingHttpClient
-
We RibbonLoadBalancingHttpClient, for example to illustrate, RibbonLoadBalancingHttpClient. The execute ()
Line 62:
/ / assembly HttpUriRequest
HttpUriRequest httpUriRequest = request.toRequest(requestConfig);
// Send an HTTP request
HttpResponse httpResponse = ((HttpClient)this.delegate).execute(httpUriRequest);
Wrap the HTTP response with the RibbonApacheHttpResponse
return newRibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI()); RibbonApacheHttpResponse by2Component components: httpResponse URICopy the code
Handling HTTP correspondence
After the HTTP request is forwarded through the above sequence, it eventually returns to the SynchronousMethodHandler, which does a sequence of processing and then responds to the browser.
-
Register Feign client beans into the IOC container
-
Looking at the Feign framework source code, we can see that the registerFeignClients() method of FeignClientsRegistar completes the registration of Feign related beans.
Feign architecture diagram
-
Step 1: Generate proxy classes based on JDK dynamic proxies.
-
Step 2: Parse out the underlying MethodHandler by declaring rules based on the interface class’s annotations
-
Step 3: Dynamically generate the request based on the RequestBean.
-
Step 4: Encoder wraps the bean as a request.
-
Step 5: The interceptor is responsible for decorating the request and return.
-
Step 6: Log.
-
Step 7: Send HTTP requests based on retries. Different HTTP frameworks are supported. By default, HttpUrlConnection is used.