Ribbon custom load balancing algorithm

What is Ribbon?

Ribbon is an open source project released by Netflix. Its main function is to provide client-side software load balancing algorithms and connect Netflix's middle-tier services together. The Ribbon client component provides a series of complete configuration items such as connection timeout, retry, etc. Simply put, it is to list all the machines behind Load Balancer (LB for short) in the configuration file, and Ribbon will automatically help you connect these machines based on certain rules (such as simple polling, random connection, etc.). It is also easy for us to use Ribbon to implement custom load balancing algorithms.

Load balancing

Load Balance means to balance and distribute the load (work tasks, access requests) to multiple operation units (servers, components) for execution. It is the ultimate solution to solve high performance, single point of failure (high availability), scalability (horizontal scalability).

Insert picture description here

Centralized LB and in-process LB

And load balancing is divided into two categories, centralized load balancing and in-process load balancing. What is centralized load balancing and in-process load balancing?

Centralized load balancing
uses independent load balancing facilities (hardware, such as F5, or software, such as nginx) between the consumer and provider of the service, and the facility is responsible for forwarding access requests through a certain strategy. Service provider

In-process load balancing
integrates the load balancing logic into the consumer. The consumer learns which addresses are available from the service registry, and then chooses a suitable server from these addresses. For example, ribbon, ribbon is just a class library, integrated in the consumer Process through which the consumer obtains the address of the service provider

How does Riboon achieve load balancing?

Take a look at the schematic diagram of Ribbon.
From the schematic diagram below, you can see that the service provider registers the service to the registry Eureka, and the service consumer uses the Ribbon to go to the service registry to obtain and query the list of these available services, and then according to the call to be called That service makes load balancing requests
Insert picture description here

Ribbon's own load balancing algorithm

Insert picture description here
Ribbon comes with 7 load balancing algorithms

1. RoundRobinRule's
default polling rule is also a strategy for avoiding many advanced rules.

2. AvailabilityFilteringRule
will filter out services that are open for fuse or services with a high number of concurrent connections

3. WeightedResponseTimeRule
gives each service a weight based on the average response time of the service. The longer the response time, the smaller the weight. When the statistics are insufficient, the polling strategy is applied.

4. RetryRule
first follow the polling strategy, if the request service fails, it will retry within the specified time

5. BestAvailableRule
first filters out the service of the circuit breaker, and then selects the one with the least concurrency

6. RandomRule
randomly obtains a service

7. ZoneAvoidanceRule is selected
according to performance and availability.

Ribbon uses the polling algorithm by default. How to modify it?

The first step is to
add a custom class, but pay attention to the storage location. This custom class cannot be placed under the current package and sub-packages scanned by @ComponentScan, otherwise our custom configuration class will be used by all Ribbon clients Shared, that is, we cannot achieve the purpose of specialization, that is to say, it cannot be placed in the same package and sub-package as the main startup.
Insert picture description here
Step 2
Add a controller on the client to call the service

@GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id")Long id){
    
    
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
    }

The third step is to
add the three services and register them to the service center.
Insert picture description here
Call /consumer/payment/get/{id} of the consumer module, and a random one appears successfully
Insert picture description here
Insert picture description here
Insert picture description here

What if we want to use our own load balancing algorithm?

Let's take a look at what is the structure of ribbon's load balancing algorithm? How is it written?

Let’s take a look at this interface first. This interface is implemented by Ribbon’s own load balancing algorithm.
Insert picture description here
Take a look at his implementation class. This abstract class implements IRule. The abstract class implements the interface to make it easier for subclasses to implement this interface. This abstract class only implements the setLoadBalancer and getLoadBalancer in the IRule interface, assigns and saves the obtained values, and facilitates the use of subclasses. These two methods can be regarded as obtaining the service information of the service center and
Insert picture description here
then take a look at Ribbon. How is his own load balancing algorithm implemented? When he implements the abstract class mentioned above, he
Insert picture description here
will first call this choose. This choose calls the getLoadBalancer of the parent class AbstractLoadBalancerRule. As mentioned above, this can be regarded as a service to obtain the service center. Information, and then call the overload method of choose.
Insert picture description here
Use lb to get the live (active) services and get all services to
Insert picture description here
get the length of the number of services, then call chooseRandomInt() to get a random subscript within the number of services, and then call Store the list of surviving services to obtain a surviving service through the subscript,
Insert picture description here
and then judge whether the acquired service is alive, if it is alive, return the service to the caller to call , if it is not alive, execute Thread.yield () The thread yields, and then waits for the next cycle to take the next service
Insert picture description here

Thread.yield() explained

The Thread.yield() method in Java thread is translated as thread concession. As the name implies, when a thread uses this method, it will give up its CPU execution time and
let itself or other threads run. Pay attention to let itself or other threads run, not simply give it to other threads .
​ The function of yield() is to make concessions. It allows the current thread "running state" into a "ready state", so that other waiting threads with the same priority access to executive power; however, does not guarantee
after the certificate in the current thread calls yield (), the other with the same priority The thread of the first level will definitely get the right to execute; it is also possible that the current thread has entered the "running state" to continue running!

Start custom load balancing

From the above source code analysis, we can see that we need to inherit the abstract implementation class AbstractLoadBalancerRule of IRule to customize the load balancing algorithm , so we can also write a class to implement it, and then set the rules in Server choose(ILoadBalancer lb, Object key)

Let's customize a polling first, instead of using the ribbon that comes with it

Write a class that inherits AbstractLoadBalancerRule , and then imitate the ribbon built-in, create an overload method of choose, note: remember to add **@Component** annotation, otherwise SpringBoot will not scan

@Component
public class MyRoundRobinRule extends AbstractLoadBalancerRule {
    
    
    //定义一个原子类,以保证原子性
    private AtomicInteger atomicInteger =new AtomicInteger(0);

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    
    
    }

    public Server choose(ILoadBalancer lb, Object key) {
    
    
        if (lb == null){
    
    
            return null;
        }
        //用于统计获取次数,当达到一定数量就不再去尝试
        int count = 0;
        Server server = null;
        //当服务还没获取到,并且尝试没有超过8次
        while (server == null && count++ < 8){
    
    
            //获取服务
            List<Server> allServers = lb.getAllServers();
            List<Server> reachableServers = lb.getReachableServers();
            int allServersSize = allServers.size();
            int reachableServersSize = reachableServers.size();
            //如果获取的服务list都为0就返回null
            if(allServersSize == 0 || reachableServersSize == 0){
    
    
                return  null;
            }
            //获取服务下标
            int next = getServerIndex(allServersSize);

            //获取服务
            server = reachableServers.get(next);

            //如果服务为空直接跳过下面的
            if (server == null){
    
    
                continue;
            }

            //如果获取到的这个服务是活着的就返回
            if (server.isAlive()){
    
    
                return server;
            }

            //如果获取到的服务不是空,但是不是存活状态,需要重新获取
            server=null;
        }

        //最后这里可能会返回null
        return  server;
    }

    //获取服务下标,为了保证原子性,使用了CAS
    public int getServerIndex(int allServersSize){
    
    
        //自旋锁
        for (;;) {
    
    
            //获取当前值
            int current = this.atomicInteger.get();
            //设置期望值
            int next = (current + 1) % allServersSize;
            //调用Native方法compareAndSet,执行CAS操作
            if (this.atomicInteger.compareAndSet(current, next))
                //成功后才会返回期望值,否则无线循环
                return next;
        }
    }
    @Override
    public Server choose(Object key) {
    
    

        return choose(getLoadBalancer(),key);
    }
}

Add
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MyRoundRobinRule.class)
configuration to the main startup class to specify your own load balancing strategy

Add @LoadBalanced to RestTemplate
Insert picture description here

Start the test, and the polling appears successfully!

Insert picture description hereInsert picture description here
Insert picture description here

Guess you like

Origin blog.csdn.net/weixin_46334920/article/details/114867996