Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the principle of Spring Cloud load balancing component Ribbon

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)05/31 Report--

This article mainly introduces the relevant knowledge of "what is the principle of Spring Cloud load balancing component Ribbon". The editor shows you the operation process through an actual case, and the operation method is simple, fast and practical. I hope this article "what is the principle of Spring Cloud load balancing component Ribbon" can help you solve the problem.

Ribbon in the Spring Cloud Netflix suite under the microservice system is mainly used for load balancing, while the underlying layer uses RestTemplate communication by default, and provides seven load balancing strategies.

Preface

In micro-services, the split of services will inevitably lead to communication requirements between micro-services, and each micro-service will deploy clusters in order to ensure high availability, so when facing a cluster micro-service to communicate, how to carry out load balancing is also an inevitable problem to consider. Then there is supply when there is demand, so a large number of excellent open source load balancing components emerge as the times require. In this paper, let's analyze the load balancing component Ribbon in Spring Cloud Netflix suite.

Thinking caused by a question

First of all, let's look at a problem. If we now have two microservices, a user-center and a user-order, I now need to invoke an interface of the user-order service in the user-center service.

In this case, we can use HttpClient,RestTemplate to initiate http requests. The user-center service port is 8001, as shown in the following figure:

@ RestController@RequestMapping (value = "/ user") public class UserController {@ Autowired private RestTemplate restTemplate; @ Bean public RestTemplate restTemplate () {return new RestTemplate ();} @ GetMapping ("/ order") public String queryOrder () {return restTemplate.getForObject ("http://localhost:8002/order/query",String.class);}})

The user-order service simply defines an interface, and the user-order service port is 8002:

RestController@RequestMapping (value = "/ order") public class UserOrderController {@ GetMapping (value = "/ query") public String queryAllOrder () {return "all orders";}}

At this time, you only need to start two services and visit http://localhost:8001/user/order to get all the order information.

As you can see, it is possible to communicate between two microservices, but what if our user-order service is a cluster? How to visit at this time? Because user-order service is already a cluster, it is necessary to have an algorithm to decide which user-order service should be requested. The simplest is random or polling mechanism, polling or random is actually a simple load balancing algorithm, and Ribbon is a component used to implement load balancing, which internally supports polling and other algorithms.

Simple use of Ribbon

Let's take a look at the simple use of Ribbon.

First, modify the user-order service to define a service name configuration in the user-order service:

Spring.application.name=user-order-service

Slightly modify the UserOrderController in the user-order service to add a port to distinguish the output:

@ RestController@RequestMapping (value = "/ order") public class UserOrderController {@ Value ("${server.port}") private int serverPort; @ GetMapping (value = "/ query") public String queryAllOrder () {return "order from:" + serverPort;}}

Start the two user-order services with the VM parameters-Dserver.port=8002 and-Dserver.port=8003, respectively.

Next, the user-center service is modified to introduce Ribbon-related dependencies into the user-center service:

Org.springframework.cloud spring-cloud-starter-netflix-ribbon 2.2.3.RELEASE

Add a Ribbon-related configuration to the user-center service, listing all the services that need to be accessed:

User-order-service.ribbon.listOfServers=\ localhost:8002,localhost:8003

Transform the UserController in the user-center service:

@ RestController@RequestMapping (value = "/ user") public class UserController {@ Autowired private RestTemplate restTemplate; @ Autowired private LoadBalancerClient loadBalancerClient; @ Bean public RestTemplate restTemplate () {return new RestTemplate ();} @ GetMapping ("/ order") public String queryOrder () {/ / get a user-order service ServiceInstance serviceInstance = loadBalancerClient.choose ("user-order-service") String url = String.format ("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort()) +" / order/query "; return restTemplate.getForObject (url,String.class);}})

At this point, we visit http://localhost:8001/user/order again and we can see that the requested user-order service will switch between 8002 and 8003.

Analysis of Ribbon principle

Looking at the above example of using Ribbon, will you find it a bit troublesome? you still need to get the ip and port yourself every time, and then format url. But in fact, we don't write code in such a primitive way in the actual development process. Let's make some changes to the above example:

@ RestController@RequestMapping (value = "/ user") public class UserController3 {@ Autowired private RestTemplate restTemplate; @ Bean @ LoadBalanced public RestTemplate restTemplate () {return new RestTemplate ();} @ GetMapping ("/ order") public String queryOrder () {return restTemplate.getForObject ("http://user-order-service/order/query",String.class);}})

In this example, it's mainly a key primary key: @ LoadBalanced.

@ LoadBalanced comment

In the @ LoadBalanced annotation, we can see that this annotation does not have any logic, but just adds a @ Qualifier annotation:

This note should be familiar to all of you. It is a common term for scenarios where multiple different names are injected into the same Bean.

@ Qualifier comment

Let's demonstrate the use of Qualifier annotations with an example.

Create a new empty TestDemo class and add a new TestConfiguration class to create a TestDemo with different names:

Configurationpublic class TestConfiguration {@ Bean ("testDemo1") public TestDemo testDemo () {return new TestDemo ();} @ Bean ("testDemo2") public TestDemo testDemo2 () {return new TestDemo ();}}

At this time, if we need to inject TestDemo, there are many ways to use it, and the specific use depends on the business needs.

Method 1: directly use @ Autowired and use the List collection to receive Bean, so that all Bean of type TestDemo will be injected.

Method 2: specify the name by using the @ Resource (name = "testDemo1") annotation so that you can inject only one Bean.

Method 3: specify a Bean by using @ Resource and @ Qualifier (value = "testDemo1"), which is basically the same as method 2.

Method 4: use @ Autowired and @ Qualifier annotations to inject without specifying any name, as shown below:

Configurationpublic class TestConfiguration {@ Bean ("testDemo1") public TestDemo testDemo () {return new TestDemo ();} @ Bean ("testDemo2") public TestDemo testDemo2 () {return new TestDemo ();}}

After running at this time, we find that no Bean will be injected into the collection, because when injecting in this way, Spring will think that currently only the Bean marked by the @ Qualifier annotation needs to be injected, and neither of the TestDemo we defined above is modified by @ Qualifier.

At this point, we only need to modify TestConfiguration slightly and add @ Qualifier modification to the definition of TestDemo:

Configurationpublic class TestConfiguration {@ Bean ("testDemo1") @ Qualifier public TestDemo testDemo () {return new TestDemo ();} @ Bean ("testDemo2") @ Qualifier public TestDemo testDemo2 () {return new TestDemo ();}}

If you run it again at this time, you will find that both testDemo1 and testDemo2 will be injected.

LoadBalancerAutoConfiguration automatic assembly

SpringCloud is implemented based on SpringBoot, so all of our commonly used distributed components are implemented based on SpringBoot auto-assembly. When we enter the LoadBalancerAutoConfiguration auto-assembly class, we can see that @ LoadBalanced is added to RestTemplate injection, which is why @ LoadBalanced can be automatically injected in our previous example:

RestTemplateCustomizer

As we can see above, RestTemplate is packaged as RestTemplateCustomizer, and the injection of RestTemplateCustomizer is as follows:

You can see that an interceptor LoadBalancerInterceptor is added to it. In fact, even if we don't look at it here, we can guess that the reason why we can communicate directly with the service name must be that the interceptor at the bottom converts it into ip, and selects the appropriate service for communication through load balancing at the bottom.

LoadBalancerInterceptor

LoadBalancerInterceptor is the default interceptor in Ribbon, so when we call the getObject method of RestTemplate, we must call the method in the interceptor.

As you can see from the source code, there is only one intercept () method in LoadBalancerInterceptor:

RibbonLoadBalancerClient#execute

Continuing to follow up on the execute method will enter the RibbonLoadBalancerClient class (initialized by the RibbonAutoConfiguration autoassembly class):

This method is also easy to understand, first get a load balancer, and then get a specified service through the getServer method, that is, when we have multiple services, we will choose a service to communicate here.

Enter the getServer method:

We see that the chooseServer method in ILoadBalancer will eventually be called, and ILoadBalancer is a top-level interface, so we need to take a look at the class diagram first.

It is impossible to see which one will be called directly from the class diagram here, but no matter which one is called, we guess that he will definitely have a place to initialize the class. In Spring, it is generally initialized in the auto-assembly class or in Configuration, and ILoadBalancer is loaded in the RibbonClientConfiguration class:

ZoneAwareLoadBalancer load balancer

The initialization of ZoneAwareLoadBalancer calls its parent class DynamicServerListLoadBalancer to initialize, and then calls the restOfInit method to initialize all services.

How to get all the services

After using Ribbon, we do not specify an ip and port when we communicate, but call the service through the service name, so the service name may correspond to multiple real services, so we must first obtain the ip and port information of all services before we can carry out load balancing processing.

There are two ways to get all services:

Get from configuration file

Get it from the Eureka registry (need to introduce the registry).

The way to initialize the service is to start a Scheduled timing task. The default is to update every 30s. In fact, it is regularly updated in this way in many source codes. Because of the simplicity of the use of the source code, it is not possible to introduce a third-party middleware to implement the timer.

The specific source code is as follows: the scheduled task started by the enableAndInitLearnNewServersFeature () method still ends up with you calling the updateListOfServers () method to update the service.

Finally, after the service is obtained, it will call the parent class BaseLoadBalancer to set all services to the allServerList collection (the BaseLoadBalancer class maintains some service-related information that the load balancer needs to use).

How to determine whether a service is available

When we get all the services in the configuration file (or Eureka registry), can we directly implement the load balancing policy for service distribution? Obviously not, because the configured service may be down (offline), resulting in service unavailability, so in BaseLoadBalancer there is not only an allServerList collection to maintain all servers, but also a collection upServerList to maintain the collection of available services, so how to determine whether a service is available? The answer is to determine whether a service is available by heartbeat testing.

Heartbeat detection Task

Before we talk about heartbeat detection, let's take a look at the setServersList method in BaseLoadBalancer. There is a logic that is more important:

This logic shows that by default, if the policy of Ping is DummyPing, then the default is upServerList = allServerList, but in fact, if we do not make any special configuration, the default is DummyPing, which is also loaded in the RibbonClientConfiguration class:

During the BaseLoadBalancer initialization process, a Scheduled scheduled task is also started to update the task regularly. Finally, like the forceQuickPing () method, a default policy is called to trigger heartbeat detection, and the default policy is DummyPing, that is, all services are available by default.

Although the real heartbeat detection operation is not performed by default, other policies such as PingUrl are provided in Netflix. PingUrl actually initiates a http request. If there is a response, the service is considered available, and if there is no response, the service is considered unavailable.

The heartbeat detection policy can be modified by the following configuration (user-order-service is the service name of the client). Since it is configurable, you can also implement a policy by yourself. You only need to implement the IPing API.

Load balancing algorithm of user-order-service.ribbon.NFLoadBalancerPingClassName=com.netflix.loadbalancer.PingUrlRibbon

When you get the available services, which one should you choose in the end? This requires the use of load balancer policy. In Ribbon, you can modify the load balancer policy by configuration, or customize the load balancer policy (which implements the IRule API).

RandomRule: random algorithm

RoundRobinRule: polling algorithm

ZoneAvoidanceRule: filter out the appropriate partition based on partition statistics (default load balancing algorithm)

RetryRule: within deadline time, if the request is unsuccessful, the request is re-initiated until an available service is found.

WeightedResponseTimeRule: the weight value is calculated according to the response time of the server. The longer the response time of the server, the smaller the weight of the server, and there will be scheduled tasks to update the weight value.

AvailabilityFilteringRule: filter out services that are short-circuited (3 consecutive connection failures) and services with high concurrency.

BestAvailableRule: select the server with the lowest number of concurrency

The load balancing algorithm can be modified through the following configuration:

This is the end of the class name of the user-order-service.ribbon.NFLoadBalancerRuleClassName=Rule rule on "what is the principle of Spring Cloud load balancing component Ribbon". Thank you for reading. If you want to know more about the industry, you can follow the industry information channel. The editor will update different knowledge points for you every day.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report