文章目录

  • 前文
  • Ribbon 负载均衡原理
  • Ribbon 源码分析
  • RoundRobinRule
  • getReachableServers()
  • getAllServers()
  • incrementAndGetModulo
  • 自定义轮询算法


Ribbon 负载均衡原理

负载均衡算法:REST 接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务启动后 REST 接口计数从 1 开始

List<ServiceInstance> instances = discoveryClient.getInstances("DEMO-PROVIDER-PAYMENT");

如:
List[0] instances = 127.0.0.1:8001
List[1] instances = 127.0.0.1:8002

1 % 2 = 1 ——》index = 1 list.get(index)
2 % 2 = 0 ——》index = 0 list.get(index)
3 % 2 = 1 ——》index = 1 list.get(index)
4 % 2 = 0 ——》index = 0 list.get(index)
5 % 2 = 1 ——》index = 1 list.get(index)

8001 + 8002 组合成为集群,共计两台机器,集群总数为 2,按照轮询算法原理:

当总请求数为 1 时:1 % 2 = 1 对应下标位置为 1,则获得服务地址为 127.0.0.1:8002
当总请求数为 1 时:2 % 2 = 0 对应下标位置为 0,则获得服务地址为 127.0.0.1:8001
当总请求数为 1 时:3 % 2 = 1 对应下标位置为 1,则获得服务地址为 127.0.0.1:8002
当总请求数为 1 时:4 % 2 = 0 对应下标位置为 0,则获得服务地址为 127.0.0.1:8001
当总请求数为 1 时:5 % 2 = 1 对应下标位置为 1,则获得服务地址为 127.0.0.1:8002

以此类推…

Ribbon 源码分析

先来看下 IRule 的 UML 图

springcold实现负载均衡代码 spring cloud 负载均衡算法_java


下面是 IRule 的接口源码

springcold实现负载均衡代码 spring cloud 负载均衡算法_java_02


IRule 的实现类

springcold实现负载均衡代码 spring cloud 负载均衡算法_java_03

RoundRobinRule

来看下默认的轮询算法源码 RoundRobinRule.java

可以发现 RoundRobinRule 类中定义了一个 AtomicInteger, 并初始化为 0

springcold实现负载均衡代码 spring cloud 负载均衡算法_java_04


再来看下 RoundRobinRule 实现 IRule 的 choose 方法

public Server choose(ILoadBalancer lb, Object key) {
	// 如果 lb 等于 null,也就是说没有负载均衡
    if (lb == null) {
        log.warn("no load balancer");
        // 返回 null
        return null;
    }

    Server server = null;
    int count = 0;
    // 如果服务器为 null 并且 count++ < 10
    while (server == null && count++ < 10) {
    	// 获取可达(健康的)的机器(服务器)
        List<Server> reachableServers = lb.getReachableServers();
        // 获取所有机器
        List<Server> allServers = lb.getAllServers();
        // 获取健康服务的数量
        int upCount = reachableServers.size();
        // 获取所有服务的数量,这里也就是上面所说的获取集群总数量如 8001 + 8002 两台机器
        int serverCount = allServers.size();

		// 一般只要有服务启动成功,通常不会执行这个 if
        if ((upCount == 0) || (serverCount == 0)) {
            log.warn("No up servers available from load balancer: " + lb);
            return null;
        }

		// 这里得到的值为 1,为什么是 1,下面会讲到
        int nextServerIndex = incrementAndGetModulo(serverCount);
        // 获取下一个服务下标,如 0、1、0、1
        server = allServers.get(nextServerIndex);

        if (server == null) {
            /* Transient. */
            Thread.yield();
            continue;
        }

        if (server.isAlive() && (server.isReadyToServe())) {
            return (server);
        }

        // Next.
        server = null;
    }

    if (count >= 10) {
        log.warn("No available alive servers after 10 tries from load balancer: "
                + lb);
    }
    // 返回下标对应的服务
    return server;
}

getReachableServers()

返回可访问的服务器

springcold实现负载均衡代码 spring cloud 负载均衡算法_springcold实现负载均衡代码_05

getAllServers()

返回所有已知的服务器,包括可访问的和不可访问的

springcold实现负载均衡代码 spring cloud 负载均衡算法_Cloud_06

incrementAndGetModulo

// 根据上面的代码如果集群数量为 2,那么这里 modulo 传的就是 2
private int incrementAndGetModulo(int modulo) {
	// 以下是一个自旋锁
    for (;;) {
        // nextServerCyclicCounter 上面已经介绍过,初始值为 0,那么也就是说 current = 0
        int current = nextServerCyclicCounter.get();
        // (0 + 1)% 2 = 1,根据上面提到的负载均衡原理,这里的下标就是 1
        int next = (current + 1) % modulo;
        // 这里使用的是 CAS (比较并交换),将当前的值(current)传进去,下一次的值传进去
        // 假设 next 值没修改过的话,那么 next 值就是 1,否则开始自旋,不断比较
        if (nextServerCyclicCounter.compareAndSet(current, next))
        	// 返回 next 值
            return next;
    }
}

自定义轮询算法

先定义一个接口

package com.java.springcloud.lb;

import org.springframework.cloud.client.ServiceInstance;
import java.util.List;

/**
 * @author Woo_home
 * @create 2020/3/27 13:13
 */

public interface LoadBalancer {
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}

创建实现类

package com.java.springcloud.lb;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Woo_home
 * @create 2020/3/27 13:14
 */

@Component // 一定要加上这个注解,否则无法扫描
public class MyLB implements LoadBalancer{

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    public final int getAndIncrement() {
        int current;
        int next;
        do {
            current = this.atomicInteger.get();
            // 这一串数字是 Integer.MAX_VALUE 的值
            next = current >= 2147483647 ? 0 : current + 1;
        } while (!this.atomicInteger.compareAndSet(current,next)); // 这里不要忘记加上 !,不然打印的结果出乎你的意料
        System.out.println("****** 第 " + next + " 次访问 ****** : ");
        // 返回的是第几次访问
        return next;
    }

    // 负载均衡算法:REST 接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务启动后 REST 接口计数从 1 开始
    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}

修改消费者端 Controller

@GetMapping(value = "/consumer/payment/lb")
public String getPaymentLB() {
	// 获取服务实例
    List<ServiceInstance> instances = discoveryClient.getInstances("DEMO-PROVIDER-PAYMENT");
    if (instances == null || instances.size() <= 0) {
        return null;
    }

	// 使用自定义的轮询算法
    ServiceInstance serviceInstance = loadBalancer.instances(instances);
    URI uri = serviceInstance.getUri();

    return restTemplate.getForObject(uri + "/payment/lb",String.class);
}

访问页面

springcold实现负载均衡代码 spring cloud 负载均衡算法_springcold实现负载均衡代码_07


springcold实现负载均衡代码 spring cloud 负载均衡算法_springcold实现负载均衡代码_08


控制台输出

springcold实现负载均衡代码 spring cloud 负载均衡算法_List_09