SpringCloud-Ribbon轮询负载规则源码解析(Day5)

轮询算法

默认负载轮训算法 : rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标(每次服务重启动后rest接口计数从1开始)

List 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
  • 如此类推…

源码分析

在这里插入图片描述

IRule接口

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();    
}

RoundRobinRule实现类

package com.netflix.loadbalancer;

import com.netflix.client.config.IClientConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * The most well known and basic load balancing strategy, i.e. Round Robin Rule.
 *
 * @author stonse
 * @author Nikos Michalakis <nikos@netflix.com>
 *
 */
public class RoundRobinRule extends AbstractLoadBalancerRule {
	
	// 定义了原子类的Integer,是线程安全的Integer
    private AtomicInteger nextServerCyclicCounter;
    // 定义了 服务存活的变量为真
    private static final boolean AVAILABLE_ONLY_SERVERS = true;
    // 定义了 所以服务的变量为假
    private static final boolean ALL_SERVERS = false;
    // 定义了 日志的对象
    private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);

	// 设置 默认的线程安全Integer为0
    public RoundRobinRule() {
        nextServerCyclicCounter = new AtomicInteger(0);
    }
	// 给lb设置负载均衡
    public RoundRobinRule(ILoadBalancer lb) {
        this();
        setLoadBalancer(lb);
    }

    //重点关注这方法。
    public Server choose(ILoadBalancer lb, Object key) {
    	// 如果负载均衡未开启为null 就提示没有负载均衡,并返回null
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }
		// 设定服务初始值为null
        Server server = null;
        // 计数数目初始为0
        int count = 0;
        // 注意下边是一个循环,我们循环获取需要提供服务的节点,最多失败10次
        while (server == null && count++ < 10) {
        	// 获取目前存活的服务主机(是真可用的服务节点,因为erueka有AP自我保护)
            List<Server> reachableServers = lb.getReachableServers();
            // 获取目前所有的服务主机
            List<Server> allServers = lb.getAllServers();
            // 设置当前存活的服务节点数量
            int upCount = reachableServers.size();
            // 设置所有的服务节点数量
            int serverCount = allServers.size();
            // 如果当前存活的主机和所有的主机都是0就提示没有可用的主机,并返回null
            if ((upCount == 0) || (serverCount == 0)) {
                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }
            // 我们调用了incrementAndGetModulo方法并传入了所有的服务节点数量
            int nextServerIndex = incrementAndGetModulo(serverCount);
            // 获取本次需要提供服务节点的索引
            server = allServers.get(nextServerIndex);
			//  如果本次需要提服务的节点如果为null,线程会进入让步状态
            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }
			// 如果需要提供服务的节点是可用状态,就使其进入准备状态并返回该服务节点
            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }
			// 设置服务节点变量为null
            // Next.
            server = null;
        }
		// 上边的循环已经进行了10次,还没有得到需要提供服务的节点,打印信息,返回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 (;;) {
        	// 获取之前已经请求的次数 (我觉得应该是上次请求提供的服务节点索引更为合适,使用‘上次索引+1’和使用‘原始请求的次数+1’都不影响的最终结果,更推荐记录上次索引的,内存占用小)
            int current = nextServerCyclicCounter.get();
            // 将之前的请求次数+1,也就是这次请求的次数了。使用本次的次数对服务节点的数量进行取模运算,得到这次请求需要的节点索引next
            int next = (current + 1) % modulo;
            // 使用CAS 比较并交换.(如果我传入current和内存中的一样,我就用next的值与其进行交换,否则将继续这个自旋锁)
            if (nextServerCyclicCounter.compareAndSet(current, next))
           		// 返回本次需要提供服务的服务节点索引
                return next;
        }
    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

临水而愚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值