象牙酥 Missing My Rainbow

RoundRibbon轮询算法-原理与源码

一、原理

默认情况下,Ribbon的负载均衡算法是RoundRibbon算法,该算法原理为:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标

注:每次服务重启动后rest接口计数从1开始。

 

假设我们的服务有2个实例,如下

List<Servicelnstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");

如:

List [0] instances = 127.0.0.1:8002
List [1] instances = 127.0.0.1:8001
8001+ 8002组合成为集群,它们共计2台机器,集群总数为2,按照轮询算法原理:

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

 

二、源码

RoundRibbon算法在类继承关系中如下图所示:

 

 

 

 

负载均衡器Ribbon中的IRule负责选择什么样的负载均衡算法

源码中IRule的接口

package com.netflix.loadbalancer;

/**
 - Interface that defines a "Rule" for a LoadBalancer. A Rule can be thought of
 - as a Strategy for loadbalacing. Well known loadbalancing strategies include
 - Round Robin, Response Time based etc.
 - 
 - @author stonse
 - 
 */
public interface IRule{
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     * 
     * @return choosen Server object. NULL is returned if none
     *  server is available 
     */

    public Server choose(Object key);

    public void setLoadBalancer(ILoadBalancer lb);

    public ILoadBalancer getLoadBalancer();    
}

 

RoundRibbon关键源码即choose方法的源码如下:

public Server choose(ILoadBalancer lb, Object key) {
    if (lb == null) {
        log.warn("no load balancer");
        return null;
    }

    Server server = null;
    int count = 0;
    while (server == null && count++ < 10) {
        List<Server> reachableServers = lb.getReachableServers();   //获取所有【可达即状态为UP在线的服务】
        List<Server> allServers = lb.getAllServers();               //获取所有的服务
        int upCount = reachableServers.size();
        int serverCount = allServers.size();

        if ((upCount == 0) || (serverCount == 0)) { //若在线服务为0,则返回null
            log.warn("No up servers available from load balancer: " + lb);
            return null;
        }

        int nextServerIndex = incrementAndGetModulo(serverCount);   //获取应该调用的服务所在reachableServerList中的index位置,由下面的incrementAndGetModulo方法的具体实现可以看到,就是取模得到的
        server = allServers.get(nextServerIndex);

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

        if (server.isAlive() && (server.isReadyToServe())) { //若获取到的server是在线的且可达的,则直接返回,否则继续while循环对下一个服务进行判断
            return (server);
        }

        // Next.
        server = null;
    }

    if (count >= 10) {
        log.warn("No available alive servers after 10 tries from load balancer: "
                + lb);
    }
    return server;
}

/**
 * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
 *
 * @param modulo The modulo to bound the value of the counter.
 * @return The next value.
 */
private int incrementAndGetModulo(int modulo) { /
    for (;;) {
        int current = nextServerCyclicCounter.get();    //当前的调用次数
        int next = (current + 1) % modulo;
        if (nextServerCyclicCounter.compareAndSet(current, next))
            return next;
    }
}

 

posted @ 2021-04-10 16:47  象牙酥  阅读(450)  评论(0编辑  收藏  举报