小程序商城开发价格- 一项创新的数字化解决方案
884
2022-11-10
【云原生&微服务八】Ribbon负载均衡策略之WeightedResponseTimeRule源码剖析(响应时间加权)
文章目录
一、前言二、WeightedResponseTimeRule
1、计算权重?
1)如何更新权重?2)如何计算权重?3)例证权重的计算
2、权重的使用
1)权重区间问题?
一、前言
前置Ribbon相关文章:
【云原生&微服务一】SpringCloud之Ribbon实现负载均衡详细案例(集成Eureka、Ribbon)【云原生&微服务二】SpringCloud之Ribbon自定义负载均衡策略(含Ribbon核心API)【云原生&微服务三】SpringCloud之Ribbon是这样实现负载均衡的(源码剖析@LoadBalanced原理)【云原生&微服务四】SpringCloud之Ribbon和Erueka集成的细节全在这了(源码剖析)【微服务五】Ribbon随机负载均衡算法如何实现的【微服务六】Ribbon负载均衡策略之轮询(RoundRobinRule)、重试(RetryRule)【微服务七】Ribbon负载均衡策略之BestAvailableRule
我们聊了以下问题:
为什么给RestTemplate类上加上了@LoadBalanced注解就可以使用Ribbon的负载均衡?SpringCloud是如何集成Ribbon的?Ribbon如何作用到RestTemplate上的?如何获取到Ribbon的ILoadBalancer?ZoneAwareLoadBalancer(属于ribbon)如何与eureka整合,通过eureka client获取到对应注册表?ZoneAwareLoadBalancer如何持续从Eureka中获取最新的注册表信息?如何根据负载均衡器ILoadBalancer从Eureka Client获取到的List
本文继续讨论 根据响应时间加权算法(WeightedResponseTimeRule)是如何实现的?
二、WeightedResponseTimeRule
WeightedResponseTimeRule继承自RoundRobinRule,也就是说该策略是对RoundRobinRule的扩展,其增加了 根据实例运行情况来计算权重 并根据权重挑选实例的规则,以达到更优的负载、实例分配效果。
下面我们一点点来看WeightedResponseTimeRule是如何实现根据相应时间计算权重并根据权重挑选实例的?
1、计算权重?
WeightedResponseTimeRule在初始化的时候会初始化父类RoundRobinRule,在RoundRobinRule的有参构造函数中会调用setLoadBalancer(ILoadBalancer)方法,WeightedResponseTimeRule类中重写了setLoadBalancer(ILoadBalancer)方法,在setLoadBalancer(ILoadBalancer)中会调用initialize(ILoadBalancer)对权重进行初始化、并定时更新。
public static final int DEFAULT_TIMER_INTERVAL = 30 * 1000;private int serverWeightTaskTimerInterval = DEFAULT_TIMER_INTERVAL;
1)如何更新权重?
WeightedResponseTimeRule通过Timer#schedule()方法启动一个上一个任务结束到下一个任务开始之间间隔30s执行一次的定时任务为每个服务实例计算权重;
定时任务的主体是DynamicServerWeightTask:
// WeightedResponseTimeRule的内部类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); } }}
DynamicServerWeightTask的run()方法中会实例化一个ServerWeight对象,并通过其maintainWeights()方法计算权重。
2)如何计算权重?
无论是权重的初始化还是权重的定时更新,都是使用ServerWeight#maintainWeights()方法来计算权重:
// WeightedResponseTimeRule的内部类class ServerWeight { public void maintainWeights() { ILoadBalancer lb = getLoadBalancer(); if (lb == null) { return; } // CAS保证只有一个线程可以进行权重的计算操作 if (!serverWeightAssignmentInProgress.compareAndSet(false, true)) { return; } try { logger.info("Weight adjusting job started"); AbstractLoadBalancer nlb = (AbstractLoadBalancer) lb; LoadBalancerStats stats = nlb.getLoadBalancerStats(); if (stats == null) { return; } // 所有实例的平均响应时间总和 double totalResponseTime = 0; for (Server server : nlb.getAllServers()) { // 汇总每个实例的平均响应时间到totalResponseTime上 ServerStats ss = stats.getSingleServerStat(server); totalResponseTime += ss.getResponseTimeAvg(); } // 计算每个实例的权重:weightSoFar + totalResponseTime - 实例的平均响应时间 // 实例的平均响应时间越长、权重就越小,就越不容易被选择到 Double weightSoFar = 0.0; List
方法的核心逻辑:
LoadBalancerStats中记录了每个实例的统计信息,累加所有实例的平均响应时间,得到总平均响应时间totalResponseTime;为负载均衡器中维护的实例列表逐个计算权重(从第一个开始),计算规则为:weightSoFar + totalResponseTime - 实例的平均响应时间;其中weightSoFar初始化为零,并且每计算好一个权重需要累加到weightSoFar上供下一次计算使用;
3)例证权重的计算
举个例子,假如服务A有四个实例:A、B、C、D,他们的平均响应时间(单位:ms)为:10、50、100、200。
服务A的所有实例的总响应时间(totalResponseTime)为:10 + 50 + 100 + 200 = 360;每个实例的权重计算规则为:总响应时间(totalResponseTime) 减去实例的平均响应时间 +累加的权重weightSoFar,具体到每个实例的计算如下:实例A:230 - 10 + 0 = 220(weightSoFar = 0)实例B:230 - 50 + 220 = 400(weightSoFar = 220)实例C:230 - 100 + 400 = 530(weightSoFar = 400)实例D:230 - 200 + 530 = 560(weightSoFar = 530)
这里的权重值表示各实例权重区间的上限,以上面的计算结果为例,它为这4个实例各构建了一个区间:
每个实例的区间下限是上一个实例的区间上限;每个实例的区间上限是我们计算出的并存储于在List
所以,根据上面示例的权重计算结果,我们可以得到每个实例的权重区间:
实例A:[0,220](weightSoFar = 0)实例B:(220, 400](weightSoFar = 220)实例C:(400, 530](weightSoFar = 400)实例D:(530, 560](weightSoFar = 530)
从这里我们可以确定每个区间的宽度实际就是:总的平均响应时间 - 实例的平均响应时间,所以服务实例的平均响应时间越短、权重区间的宽度就越大,服务实例被选中的概率就越高。
这些区间边界的开闭如何确定?区间在哪里使用?
2、权重的使用
我们知道Ribbon负载均衡算法体现在IRule的choose(Object key)方法中,而choose(Object key)方法中又会调用choose(ILoadBalancer lb, Object key)方法,所以我们只需要看WeightedResponseTimeRule的choose(ILoadBalancer lb, Object key)方法:
方法的核心流程如下:
如果服务实例的最大权重值 < 0.001 或者服务的实例个数发生变更,则采用父类RoundRobinRule做轮询负载;否则,利用Random函数生成一个随机数randomWeight,然后遍历权重列表,找到第一个权重值大于等于随机数randomWeight的列表索引下标,然后拿当前权重列表的索引值去服务实例列表中获取具体实例。
1)权重区间问题?
正常每个区间都为(x, y],但是第一个实例和最后一个实例不同:
由于随机数的最小取值可以为0,所以第一个实例的下限是闭区间;随机数的最大值取不到最大权重值,所以最后一个实例的上限是开区间;
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~