[SpringCloud] Eureka call link process analysis based on Ribbon load balancing

Preface

On the basis of mutual calls between microservices, the calls between services are more in the form of calling an instance under a multi-instance service. And this requires the use of load balancing technology. For developers, load balancing can be enabled through the @LoadBalance annotation. I think you also want to know what the bottom layer of such a simple operation looks like.

1. Call form

Based on "SpringCloud integrates Eureka and implements load balancing", we can conduct a small experiment and debug the running program , initiate a request through postman, and service A will remotely call service B. Debug found that the URL sent is: http://user-service/user/1. There is no doubt that this is the way A calls service B Combined with the integrated load balancing process, this must be Ribbon at work We found that the request could not be sent because there was something wrong with the path. Observing the parameter user-service in the path, we found that it is the service name of service B. Then why can we respond normally when we send a request in the form of "service name-interface path-parameter" from service A to service B?
Insert image description here
Similarly, after getting this url, we went to postman to send the request:
Insert image description here

2.LoadBalancerInterceptor

The premise of load balancing is not to pass a specific URL. Ribbon must have done some kind of parsing, and obtained the instance list under the service through the service name, thereby pulling the service registry in Eureka-Server to transfer the request. Map to a specified instance.
Combined with the experience of web development that separated front-end and back-end, the back-end often intercepts requests from the front-end in the interceptor to perform some operations on the request, such as verification, splicing, authentication... and calls. The request sent by the party and the request received by the receiver are not consistent. Is there something similar to an interceptor that intercepts the request and converts the request?
The answer is inevitable, who is it - LoadBalancerInterceptor
Insert image description here
What you can see is that he implements the ClientHttpRequestInterceptor interface. For specific usage details, go directly to the declaration in the interface. method
It can be seen intuitively that the interface declares an intercept() method and accepts the HttpRequest parameter to intercept the client's http request and modify the request body! The answer to the URL change is revealed here, so how is the bottom layer of the method implemented:

3. Load balancing process analysis

3.1 Call flow chart

Before debugging the source code, let’s take a look at the overall flow chart (hand diagram) of the call link in the source code:
Insert image description here
In summary, it is: intercept request-read service-pull Service list—selection rules—return service instance

3.2 intercept() method

Let’s start debugging:
1. When sending a request to cause a calling relationship between services, the calling request will first be passed to in the interceptor intercept method, you can see that it is still consistent with sending
Insert image description here
2. Continue to execute, start parsing the request, and get the URI in the request ——Get the host address (name of the service) through the getHost() method
Insert image description here

3.3 execute() method

3. Ribbon starts load balancing processing
Insert image description here
4. After entering twice, enter the execute() method and find that the service name passed in is entered into getLoadBalance() as the service Id. ) method, and got an ILoadbalance interface object, which encapsulates a lot of information:
Insert image description here
here Remember the value of the service instance id:host.docker.internal:8084, this is the instance information received by the Eureka client

3.4 getServer() method

5. The interface object is passed as a parameter to the getServer() method, and a server object is obtained and entered into the method. It was found that an object of type Object was passed at the same time to specify the rules or conditions of the server. However, so far, this parameter has been passed as null, that is, the loadBalancer.chooseServer() method uses the 'default' method. choose
Insert image description here

3.4 Subclass chooseServer() method

6. Step into the chooseServer() method again and find that it is a parent class method rewritten under a class called BaseLoadBalancer (this class is the specific implementation of the load balancer and will be analyzed in detail later)
At this time: what can be judged is getLoadBalancerStats().getAvailableZones().size() <= 1 is TRUE
Insert image description here

3.5 getLoadBalancerStats().getAvailableZones().size() <= 1

For expression: getLoadBalancerStats().getAvailableZones().size() <= 1 for analysis
It is found that in the BaseLoadBalancer class, it inherits the abstract class AbstractLoadBalancer and overrides getLoadBalancerStats( ) abstract method, obtained a loadbalancer statistical information collection LoadBalancerStats
Insert image description here
Insert image description here
and the information encapsulated in LoadBalancerStats has a collection attribute of the ConcurrentHashMap type, that is

volatile Map<String, List<? extends Server>> upServerListZoneMap = new ConcurrentHashMap<String, List<? extends Server>>();

is used to store the list of available services. Each entry in this collection represents a region. The key is the region name and the value is List of servers available in this zone.
Insert image description here
The subsequent .getAvailableZones() method obtains all the keys in this attribute value, that is, the available service areas, and returns them as a Set collection for judgment.
Insert image description here
Obviously, here is further evidence that getLoadBalancerStats().getAvailableZones().size() <= 1 is true, and the chooseServer() method of the parent class will be called later

3.6 ChooseServer() method of parent class

7. Step into the chooseServer() method of the parent class and find that a Server type object is finally returned. This must be the specific service instance information.
Insert image description here

3.7 Examples under the IRule interface

traced the rule variable and found that it was an instance of the IRule interface, which is an interface that provides rules for load balancing
Insert image description here
and there are a large number of rule implementations under this interface, and The default rule method is polling scheduling:
Insert image description here
However, when debugging in the above figure, the gray color on the right side of the rule variable is rule:RandomRule@12045This is because I specified the load balancing rules by configuring the IRule type Bean:

    @Bean
    public IRule randomRule() {
    
    
        return new RandomRule();
    }

As long as you comment it out, the program will continue to use the default rulesRoundRobinRule

3.8 The final choose() method—return server

8. After understanding the rule instance of the IRule interface, let’s look at the choose() method it finally calls. Step in the same way. Since it is the default rule, follow the process and enter the choose method in the RoundRobinRule rule implementation (in fact, every rule implementation class under the IRule interface has a choose method)
Insert image description here
Implemented ILoadBalancerThe load balancer object of the interface is passed to the method as a parameter, and the key is default. Start warming up for random selection.

3.9 Internal analysis of choose() method

9. Enter the while loop and continue to select servers until an available server is found. Then it willdetermine whether the thread is interrupted. If it is interrupted, null will be returned directly.
Insert image description here
This situation still occurs very frequently. It can be seen that this small design will reduce a lot of unnecessary calculations and improve the efficiency of program operation.
Then this is to obtain two service lists respectivelyInsert image description here
Select one from the list
Insert image description here
Go through the operation and make a judgment on the choice< /span>Insert image description here
Finally, the Server instance was successfully returned to the chooseServer() method. The service initiator sent the http://user-service/user request through Ribbon and finally polled the localhost:8084 service instance

4. Easter eggs

Many companies are now using Nacos to replace Eureka, because it is not sensitive enough to detect changes in the service list, and it is too slow to detect offline services, as in the following situation:
Insert image description here
Service The instance has been offline for about a minute, but the offline service is still loaded into the available service list (upList). In fact, this is not Ribbon's fault, it is all Eureka's fault
We’ll leave an Easter egg for this situation, and we’ll talk about it next time~

Guess you like

Origin blog.csdn.net/weixin_57535055/article/details/134449104