Ribbon负载均衡策略(1)

Ribbon负载均衡策略(1)

Ribbon负载均衡策略(2)

Ribbon负载均衡策略

AbstractLoadBalancerRule

抽象类里定义了负载均衡器ILoadBalancer,该对象能够在具体实现选择服务策略时,获取到一些负载均衡中维护的信息,来作为分配依据,并以此设计一些算法实现针对特定场景的高效策略。

public abstract class AbstractLoadBalancerRule implements IRule, IClientConfigAware {

    private ILoadBalancer lb;
        
    @Override
    public void setLoadBalancer(ILoadBalancer lb){
        this.lb = lb;
    }
    
    @Override
    public ILoadBalancer getLoadBalancer(){
        return lb;
    }      
}

负载均衡策略

RandomRule 随机选择

实现IRule接口choose()方法,通过调用AbstractLoadBalancerRule.getLoadBalancer()获取负载均衡器,委托给自身choose()方法来处理。通过rand.nextInt()方法,从有用的服务实例中随机选择。(Randomly choose from all living servers)

public class RandomRule extends AbstractLoadBalancerRule {
    Random rand;
	@Override
	public Server choose(Object key) {
	    //实现IRule接口choose()方法,通过调用AbstractLoadBalancerRule.getLoadBalancer()获取负载均衡器,委托给自身choose()方法来处理
		return choose(getLoadBalancer(), key);
	}

    public Server choose(ILoadBalancer lb, Object key) {
        //todo lb非空判断
        Server server = null;
        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> upList = lb.getReachableServers();
            List<Server> allList = lb.getAllServers();

           //todo 无注册实例 返回null

			//从健康实例中 随机选择
            int index = rand.nextInt(serverCount);
            server = upList.get(index);

            if (server == null) {
                Thread.yield();
                continue;
            }
            if (server.isAlive()) {
                return (server);
            }
            server = null;
            Thread.yield();
        }
        return server;
    }
	
}

RoundRobinRule 线性轮询

与RandomRule差不多,不过只尝试10次去获取有用的服务实例;调用的是incrementAndGetModulo()线性轮询实例。

 public Server choose(ILoadBalancer lb, Object key) {
     Server server = null;
     int count = 0;
     //尝试10次放弃
     while (server == null && count++ < 10) {
         
		//线性轮询
         int nextServerIndex = incrementAndGetModulo(serverCount);
         server = allServers.get(nextServerIndex);

         // Next.
         server = null;
     }

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

RetryRule 重试机制

该策略实现了一个具备重试机制的实例选择功能。
可以选择使用哪个基础策略,默认使用RoundRobinRule(线性轮询),在结束时间内进行反复尝试的策略。
结束时间 :long deadline = requestTime + maxRetryMillis;

/*
	 * Loop if necessary. Note that the time CAN be exceeded depending on the
	 * subRule, because we're not spawning additional threads and returning
	 * early.
	 */
	public Server choose(ILoadBalancer lb, Object key) {
		long requestTime = System.currentTimeMillis();
		long deadline = requestTime + maxRetryMillis;

		Server answer = null;
		answer = subRule.choose(key);
		//反复尝试
		if (((answer == null) || (!answer.isAlive()))
				&& (System.currentTimeMillis() < deadline)) {

			InterruptTask task = new InterruptTask(deadline
					- System.currentTimeMillis());

			while (!Thread.interrupted()) {
				answer = subRule.choose(key);

				if (((answer == null) || (!answer.isAlive()))
						&& (System.currentTimeMillis() < deadline)) {
					/* pause and retry hoping it's transient */
					Thread.yield();
				} else {
					break;
				}
			}

			task.cancel();
		}

		if ((answer == null) || (!answer.isAlive())) {
			return null;
		} else {
			return answer;
		}
	} 

WeightedResponseTimeRule 权重

该策略是继承RoundRobinRule,对原有的进行扩展。它的实现有3个核心内容:定时任务、权重计算、实例选择。响应时间越短,权重越高

定时任务

在WeightedResponseTimeRule初始化(initialize)的时候,会通过设置一个定时任务,为每个服务实例计算权重,serverWeightTaskTimerInterval默认30s执行1次。
serverWeightTimer.schedule(new DynamicServerWeightTask(), 0,serverWeightTaskTimerInterval);

class DynamicServerWeightTask extends TimerTask {
        public void run() {
            ServerWeight serverWeight = new ServerWeight();
            try {
                //计算权重
                serverWeight.maintainWeights();
            } catch (Exception e) {
                logger.error("Error running DynamicServerWeightTask for {}", name, e);
            }
        }
    }
权重计算 serverWeight.maintainWeights()

WeightedResponseTimeRule里有个volatile List<Double> accumulatedWeights的成员变量,是用于存储权重,与负载均衡器的服务实例清单的位置一一对应。

public void maintainWeights() {
    ILoadBalancer lb = getLoadBalancer();
   	//todo ...
    try {
        logger.info("Weight adjusting job started");
        AbstractLoadBalancer nlb = (AbstractLoadBalancer) lb;
        LoadBalancerStats stats = nlb.getLoadBalancerStats();
        // no statistics, nothing to do return ...
        
        //计算所有实例的平均响应时间的总和totalResponseTime 
        double totalResponseTime = 0;
        // find maximal 95% response time
        for (Server server : nlb.getAllServers()) {
            // this will automatically load the stats if not in cache
            ServerStats ss = stats.getSingleServerStat(server);
            totalResponseTime += ss.getResponseTimeAvg();
        }
        
        // so that the longer the response time, the less the weight and the less likely to be chosen
        Double weightSoFar = 0.0;
        
        // create new list and hot swap the reference
        List<Double> finalWeights = new ArrayList<Double>();
        for (Server server : nlb.getAllServers()) {
            ServerStats ss = stats.getSingleServerStat(server);
            //weight for each server is (sum of responseTime of all servers - responseTime)
            //实例区间宽度=总的响应时间 - 实例的平均响应时间
            double weight = totalResponseTime - ss.getResponseTimeAvg();
            //计算权重区间 如A[0,100] B(100,200] C(200,360]
            weightSoFar += weight;
            finalWeights.add(weightSoFar);   
        }
		//设置权重成员accumulatedWeights
        setWeights(finalWeights);
    } catch (Exception e) {
        logger.error("Error calculating server weights", e);
    } finally {
        serverWeightAssignmentInProgress.set(false);
    }

}
实例选择

获取最后一个实例权重,得知整个标尺大小,满足要求(>=0.001),就从标尺里间随机选择1个数,落在哪服务实例的区间就选择哪个实例。如A[0,100] B(100,200] C(200,360]

标尺

public Server choose(ILoadBalancer lb, Object key) {
    //todo ...
    while (server == null) {
        // get hold of the current reference in case it is changed from the other thread
        List<Double> currentWeights = accumulatedWeights;
        //todo ...
        List<Server> allList = lb.getAllServers();
        int serverCount = allList.size();
        if (serverCount == 0) {
            return null;
        }
        int serverIndex = 0;
        // last one in the list is the sum of all weights 获取最后一个实例的权重,即标尺总长
        double maxTotalWeight = currentWeights.size() == 0 ? 0 : currentWeights.get(currentWeights.size() - 1); 
        // No server has been hit yet and total weight is not initialized
        // fallback to use round robin
        if (maxTotalWeight < 0.001d) {
            //如果maxTotalWeight <0.001,则调用直父类RoundRobinRule的选择策略
            server =  super.choose(getLoadBalancer(), key);
            if(server == null) {
                return server;
            }
        } else {
            // generate a random weight between 0 (inclusive) to maxTotalWeight (exclusive)
            double randomWeight = random.nextDouble() * maxTotalWeight;
            // pick the server index based on the randomIndex
            int n = 0;
            for (Double d : currentWeights) {
                //遍历权重清单,若权重大于或等于随机权重数,则返回该实例索引值
                if (d >= randomWeight) {
                    serverIndex = n;
                    break;
                } else {
                    n++;
                }
            }

            server = allList.get(serverIndex);
        }

		//todo ...
    }
    return server;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值