文章目录

  • 一、前言
  • 二、WeightedResponseTimeRule
    • 1、计算权重?
      • 1)如何更新权重?
      • 2)如何计算权重?
      • 3)例证权重的计算
    • 2、权重的使用
      • 1)权重区间问题?

一、前言

前置Ribbon相关文章:

  1. 【云原生&微服务一】SpringCloud之Ribbon实现负载均衡详细案例(集成Eureka、Ribbon)
  2. 【云原生&微服务二】SpringCloud之Ribbon自定义负载均衡策略(含Ribbon核心API)
  3. 【云原生&微服务三】SpringCloud之Ribbon是这样实现负载均衡的(源码剖析@LoadBalanced原理)
  4. 【云原生&微服务四】SpringCloud之Ribbon和Erueka集成的细节全在这了(源码剖析)
  5. 【微服务五】Ribbon随机负载均衡算法如何实现的
  6. 【微服务六】Ribbon负载均衡策略之轮询(RoundRobinRule)、重试(RetryRule)
  7. 【微服务七】Ribbon负载均衡策略之BestAvailableRule

我们聊了以下问题:

  1. 为什么给RestTemplate类上加上了@LoadBalanced注解就可以使用Ribbon的负载均衡?
  2. SpringCloud是如何集成Ribbon的?
  3. Ribbon如何作用到RestTemplate上的?
  4. 如何获取到Ribbon的ILoadBalancer?
  5. ZoneAwareLoadBalancer(属于ribbon)如何与eureka整合,通过eureka client获取到对应注册表?
  6. ZoneAwareLoadBalancer如何持续从Eureka中获取最新的注册表信息?
  7. 如何根据负载均衡器ILoadBalancer从Eureka Client获取到的List<Server>中选出一个Server?
  8. Ribbon如何发送网络HTTP请求?
  9. Ribbon如何用IPing机制动态检查服务实例是否存活?
  10. Ribbon负载均衡策略之随机(RandomRule)、轮询(RoundRobinRule)、重试(RetryRule)、选择并发量最小的(BestAvailableRule)实现方式;

本文继续讨论 根据响应时间加权算法(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<Double> finalWeights = new ArrayList<Double>();for (Server server : nlb.getAllServers()) {ServerStats ss = stats.getSingleServerStat(server);double weight = totalResponseTime - ss.getResponseTimeAvg();weightSoFar += weight;finalWeights.add(weightSoFar);   }setWeights(finalWeights);} catch (Exception e) {logger.error("Error calculating server weights", e);} finally {// 表示权重计算结束,允许其他线程进行权重计算serverWeightAssignmentInProgress.set(false);}}
}

方法的核心逻辑:

  1. LoadBalancerStats中记录了每个实例的统计信息,累加所有实例的平均响应时间,得到总平均响应时间totalResponseTime
  2. 为负载均衡器中维护的实例列表逐个计算权重(从第一个开始),计算规则为:weightSoFar + totalResponseTime - 实例的平均响应时间;
  3. 其中weightSoFar初始化为零,并且每计算好一个权重需要累加到weightSoFar上供下一次计算使用;

3)例证权重的计算

举个例子,假如服务A有四个实例:A、B、C、D,他们的平均响应时间(单位:ms)为:10、50、100、200。

  • 服务A的所有实例的总响应时间(totalResponseTime)为:10 + 50 + 100 + 200 = 360
  • 每个实例的权重计算规则为:总响应时间(totalResponseTime) 减去 实例的平均响应时间 + 累加的权重weightSoFar,具体到每个实例的计算如下:
  1. 实例A:360 - 10 + 0 = 350(weightSoFar = 0)
  2. 实例B:360 - 50 + 350 = 660(weightSoFar = 350)
  3. 实例C:360 - 100 + 660 = 920(weightSoFar = 660)
  4. 实例D:360 - 200 + 920 = 1080(weightSoFar = 920)

这里的权重值表示各实例权重区间的上限,以上面的计算结果为例,它为这4个实例各构建了一个区间:

  1. 每个实例的区间下限是上一个实例的区间上限;
  2. 每个实例的区间上限是我们计算出的并存储于在List<Double>类型的accumulatedWeights变量中的权重值,其中第一个实例的下限默认为零。

所以,根据上面示例的权重计算结果,我们可以得到每个实例的权重区间:

  1. 实例A:[0,350](weightSoFar = 0)
  2. 实例B:(350, 660](weightSoFar = 350)
  3. 实例C:(660, 920](weightSoFar = 660)
  4. 实例D:(920, 1080](weightSoFar = 920)

从这里我们可以确定每个区间的宽度实际就是:总的平均响应时间 - 实例的平均响应时间,所以服务实例的平均响应时间越短、权重区间的宽度就越大,服务实例被选中的概率就越高。

这些区间边界的开闭如何确定?区间在哪里使用?

2、权重的使用

我们知道Ribbon负载均衡算法体现在IRule的choose(Object key)方法中,而choose(Object key)方法中又会调用choose(ILoadBalancer lb, Object key)方法,所以我们只需要看WeightedResponseTimeRule的choose(ILoadBalancer lb, Object key)方法:

方法的核心流程如下:

  1. 如果服务实例的最大权重值 < 0.001 或者服务的实例个数发生变更,则采用父类RoundRobinRule做轮询负载;
  2. 否则,利用Random函数生成一个随机数randomWeight,然后遍历权重列表,找到第一个权重值大于等于随机数randomWeight的列表索引下标,然后拿当前权重列表的索引值去服务实例列表中获取具体实例。

1)权重区间问题?

正常每个区间都为(x, y],但是第一个实例和最后一个实例不同:

  1. 由于随机数的最小取值可以为0,所以第一个实例的下限是闭区间;
  2. 随机数的最大值取不到最大权重值,所以最后一个实例的上限是开区间;

【云原生微服务八】Ribbon负载均衡策略之WeightedResponseTimeRule源码剖析(响应时间加权)相关推荐

  1. 最新版Spring Cloud Alibaba微服务架构-Ribbon负载均衡篇

    文章目录 前言 一.Ribbon核心概念 二.服务器端负载均衡和Riboon客户端负载均衡 1.服务器端负载均衡: 2.Riboon客户端负载均衡: 三.Ribbon策略 四.Ribbon配置使用 五 ...

  2. 波司登云原生微服务治理探索

    作者:山猎,珑乘 01 背景 Aliware 波司登创始于1976年,专注于羽绒服的研发.设计.制作,是全球知名的羽绒服生产商.波司登用一系列世人瞩目的辉煌成绩证明了自己的实力:连续26年全国销量领先 ...

  3. 从建好到用好,阿里云原生微服务生态的演进

    作者:微服务小组 本文整理自阿里云智能高级技术专家彦林的线上直播分享<阿里云原生微服务生态与规划>.视频回放地址:https://yqh.aliyun.com/live/detail/28 ...

  4. Dubbo 3.0 - 开启下一代云原生微服务

    作者 | 郭浩(项升)  阿里巴巴经济体 RPC 框架负责人 **导读:**本文整理自作者于 2020 年云原生微服务大会上的分享<Dubbo3.0 - 开启下一代云原生微服务>,主要介绍 ...

  5. 干货 | 基于开源体系的云原生微服务治理实践与探索

    作者简介 CH3CHO,携程高级研发经理,负责微服务.网关等中间件产品的研发工作,关注云原生.微服务等技术领域. 一.携程微服务产品的发展历程 携程微服务产品起步于2013年.最初,公司基于开源项目S ...

  6. 基于开源体系的云原生微服务治理实践与探索

    作者:董艺荃|携程服务框架负责人 携程微服务产品的发展历程 携程微服务产品起步于 2013 年.最初,公司基于开源项目 ServiceStack 进行二次开发,推出 .Net 平台下的微服务框架 CS ...

  7. 云原生微服务架构实战精讲第二节 云原生和Kubernete

    第03讲:云原生应用的 15 个特征 本课时我将带你学习云原生应用. 微服务架构只是一种软件架构风格,并不限制所采用的实现技术,开发团队可以自由选择最合适的技术来实现.在第 01 课时介绍微服务架构的 ...

  8. 秒杀springboot——未来轻量级高性能的Java云原生微服务框架来啦

    秒杀springboot--未来轻量级高性能的Java云原生微服务框架来啦 引子 自2003年Rod.Juergen 和 Yann开发并发布Spring项目后,J2EE 迎来了新的开始.在 2013 ...

  9. 【云原生微服务>SCG网关篇十二】Spring Cloud Gateway集成Sentinel API实现多种限流方式

    文章目录 一.前言 二.Gateway集成Sentinel API 0.集成Sentinel的核心概念 1)GatewayFlowRule 和 ApiDefinition 2)GatewayFlowR ...

最新文章

  1. 【数据结构】队列之链队列
  2. [OS复习]文件管理2
  3. 汇编语言-学习笔记(一)
  4. docker用gpu的参数_Docker化部署(GPU)
  5. java创作2019-7-19日报管理系统
  6. linux查看进程加载的jar包,[Linux] 查看jar包内容
  7. linux 解压 WinRAR 压缩文件
  8. IDEA中下载mybatis插件mybatis plugins 离线版安装
  9. 电脑本地连接,电脑出现多个“本地连接”的解决方法
  10. STC89C52单片机学习笔记(一)
  11. BZOJ 2751: [HAOI2012]容易题(easy)( )
  12. CUD表单验证+增删改查
  13. 六、Python函数
  14. 2018.8.盛夏,
  15. 自制3DSMAX 自动减面脚本
  16. file 转换MultipartFile
  17. 火狐浏览器打开b站默认静音解决办法
  18. php7 beast,HP源码加密工具(php-beast)php7版
  19. 使用kegg数据库的一点见解
  20. search 搜索框 html css 超级居中

热门文章

  1. 有哪些问卷调查工具推荐?
  2. 将自己的python代码打包成exe的可执行文件
  3. Shell脚本入门 03:字符串与数组
  4. 文档的新建,打开与保存
  5. 国内大数据公司排名你知道几个?
  6. Cesium 地图分级分片显示
  7. 转自知乎的吉他学习篇
  8. C++实现经典坦克大战(一)
  9. 舟山惠普服务器维修电话,【舟山HP(惠普)服务器配件】舟山HP(惠普)服务器配件报价及图片大全-列表版-ZOL中关村在线...
  10. ubuntu20.04屏幕亮度无法调节的解决方法->安装 brightness-controller-simple 软件