轮询算法round-robin是很基础的负载均衡算法,实际应用中wrr更为常见,但一般不需要自己实现,因为一般需要rr的场景,都已经在基础设施层面进行了支持,比如lvs或nginx通过配置即可实现,但业务上偶尔也需要自己实现负载均衡,所以有必要了解一下其技术原理。

谈到wrr的应用场景,一般是服务器配置存在差异时,比如集群里有一个2C4G和一个4C8G,那么我们希望4C8G能承担更多的业务请求。或者是,我们希望通过无条件的流量分发,在集群的一台机器上小范围的导入一些流量进行一些实验。我碰到的场景是,在网关层面对业务方的集群迁移进行支持,也属于第二种场景。

原理

权重可以看做期望目标出现的概率,比如两个节点A、B,权重分别是100和50,那么等于在说,我们期望66.7%的请求由A处理,剩余33.3%的请求由B处理。

一种比较挫的实现方式,是将所有节点按照权重比例分布在一个离散的区间内,比如1-100,然后使用随机数生成100以内的自然数,如果随机数0<x<=66就由A处理请求,如果66<x<=100就由B处理请求。这种方法看似实现容易,但致命缺陷是依赖随机数的有效性,而大部分场景我们使用的都是伪随机数。此外集群数量伸缩时,会出现精度问题。

在wiki中提供了两种wrr的实现方式,分别是classic wrr和interleaved wrr,简称为WRR和IWRR,他们的区别可以通过下图来了解一下。

  • WRR
    初始权重水位c设置为0,外循环遍历所有的节点,内循环遍历当前节点的水位值,如果权重水位c在该值内,则使用当前节点处理请求,同时c值递增。直到c大于当前节点的权重值,重置c为0,再对下一个节点执行内循环。

  • IWRR
    外循环递增水位值,内循环遍历所有节点,如果当前水位值低于节点权重值,则由当前节点处理请求。遍历完所有节点后,递增水位值后再对所有节点进行遍历。

通过对比可以发现,如果集群中的节点权重相近,且值都比较高,则使用IWRR会明显的避免节点负载的尖刺情况,节点越多越明显。但这种情况会随着节点权重差异的增加而消退。极端假设集群中有一个节点权重是100,其他节点权重都是1,那么IWRR和WRR的效果是一样的。

此外,在IWRR的实践中,一般会用集群内节点权重值的最大公约数(gcd),来作为外循环水位值的递增步长,这样可以减轻少量权重较低节点的摆动。比如集群内有4个权重为100的节点ABCD,1个权重为20的节点E,当步长为1时,节点E会在开始的20个周期内执行20次,然后在剩下的80个周期内无事可做。如果使用gcd来作为步长,最多隔5个周期就会处理一次请求。

实践

这里用go实现了一个类IWRR的负载均衡器,区别在于横向扫描的方向是自顶向下的

package balancerimport ("errors"
)var (ErrSuccess  = error(nil)ErrNoTarget = errors.New("no target to run")
)type ITarget interface {Call() error // 实际工作负载方法Weight() int // 当前target权重
}type Balancer struct {targets   []ITarget // 目标集maxweight int       // 当前集群中的最大权重current   int       // 当前权重水位stride    int       // 权重步长(最大公约数)index     int       // 当前目标索引
}func (b *Balancer) AddTarget(t ITarget) {// 更新目标集b.targets = append(b.targets, t)// 更新步长b.stride = t.Weight()b.maxweight = t.Weight()for _, v := range b.targets {b.stride = b.gcd(b.stride, v.Weight())// 保存目标集中最大权重值if v.Weight() > b.maxweight {b.maxweight = v.Weight()}}// 更新当前水位b.current = b.maxweight// 更新当前索引位置b.index = 0
}func (b *Balancer) Call() error {if len(b.targets) == 0 {return ErrNoTarget}// 查找满足当前权重水位的targeti := b.indexfor {// 取得target对象target := b.targets[i]i++if target.Weight() >= b.current {target.Call()break}if i == len(b.targets) {// 重置索引、降低水位i, b.current = 0, b.current-b.strideif b.current <= 0 {// 重置当前水位b.current = b.maxweight}}}if i == len(b.targets) {// 重置索引、降低水位i, b.current = 0, b.current-b.strideif b.current <= 0 {// 重置当前水位b.current = b.maxweight}}b.index = ireturn ErrSuccess
}// 辗转相除求最大公约数
func (b *Balancer) gcd(m, n int) int {if n == 0 {return m}return b.gcd(n, m%n)
}

使用方法如下:这里创建了三个节点,权重分别是100、50、20,期望的分布是10:5:2,所以在后面的循环里打印了17次。

package mainimport ("bournex/weightbalance/balancer""fmt"
)// 虚构的集群目标节点
type Target struct {name   stringweight int
}// 虚构的执行过程
func (t Target) Call() error {fmt.Printf("%s", t.name)return nil
}// 获取当前目标的权重值
func (t Target) Weight() int {return t.weight
}func main() {var (x = Target{name: "A", weight: 100}y = Target{name: "B", weight: 50}z = Target{name: "C", weight: 20}b balancer.Balancer)if err := b.Call(); err != nil {fmt.Println(err.Error())}b.AddTarget(x)b.AddTarget(y)for i := 0; i < 17; i++ {b.Call()}fmt.Println()b.AddTarget(z)for i := 0; i < 17; i++ {b.Call()}fmt.Println()
}

执行输出如下

# ./app
no target to run
AABAABAABAABAABAA
AAAAAABABABABCABC

第一行打印由于还没有添加Target,所以当前集群没有可以执行任务的节点,输出error内容"no target to run"。
第二行是节点AB处理负载的情况,以AAB为模式循环处理请求。满足100:50的负载均衡需求。
第三行是添加了节点Z的结果,总共输出17次,A10次,B5次,C2次

作为对比,如果采用1替换gcd作为步长实现IWRR,则它的模式是这样的:

ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

可以看到,C要隔很久才会再次出现

100行代码实现加权负载均衡算法(WRR)相关推荐

  1. 负载均衡算法WRR介绍

    一.负载均衡 负载均衡是一个很大的概念,既有从硬件层面来解决问题的,又有从软件层面解决的,有关负载均衡的介绍,推荐阅读: http://os.51cto.com/art/201108/285359.h ...

  2. 互联网研发中负载均衡算法一点探索

    负载均衡在线上服务中有着很重要作用,因为一台web服务比如tomcat,能够处理qps(每秒处理请求数) 是有限的.那么就需要有有前端负载均衡服务将大的流量分发为多个后端服务进行处理. 负载均衡产品有 ...

  3. 负载均衡算法及其Java代码实现

    负载均衡算法及其Java代码实现 什么是负载均衡 负载均衡,英文 名称为Load Balance,指由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须 ...

  4. 算法高级(13)-常见负载均衡算法Java代码实现

    我们在分布式系统常见负载均衡算法中对负载均衡及负载均衡算法进行了介绍,接下来我们用代码对常见的几种算法进行实现. 本文讲述的是"将外部发送来的请求均匀分配到对称结构中的某一台服务器上&quo ...

  5. java 路由算法_几种简单的负载均衡算法及其Java代码实现

    什么是负载均衡 负载均衡,英文 名称为Load Balance,指由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须其他服务器的辅助.通过某种 负载分 ...

  6. 负载均衡算法--加权轮询法(Weight Round Robin)

    接上一篇博文:负载均衡算法–轮询法(Round Robin),本文讲解加权轮询算法. 加权轮询算法:不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同.给配置高.负载低 ...

  7. java负载均衡原理_多种负载均衡算法及其 Java 代码实现

    首先给我们介绍下什么是负载均衡 负载均衡 树立在现有网络结构之上,它供给了一种廉价有用通明的办法扩展 网络设备和 效劳器的带宽.添加 吞吐量.加强网络数据处理才能.进步网络的灵敏性和可用性. 负载均衡 ...

  8. 自古帝王多短命,假如皇帝也懂负载均衡算法...

    " 大家都知道古代皇帝各个都是后宫佳丽三千,而皇帝身上都天然的带着雨露均沾的精神,不想单独的宠爱一人! 来自:51cto技术栈 弱水三千,又怎舍得只取一瓢饮?据传皇帝们晚上睡觉个个都怕冷,因 ...

  9. 假如古代皇帝也懂负载均衡算法

     大家都知道古代皇帝各个都是后宫佳丽三千,而皇帝身上都天然的带着雨露均沾的精神,不想单独的宠爱一人!   溺水三千,又怎舍得只取一瓢饮?据传皇帝们晚上睡觉个个都怕冷,因此每晚都需要有人侍寝,那么这么多 ...

最新文章

  1. 【青少年编程】黄羽恒:平行空间
  2. 麒麟芯片AI首席科学家,解读AI芯片如何让手机更智能
  3. python中Dict与OrderedDict
  4. Saving James Bond - Easy Version 原创 2017年11月23日 13:07:33
  5. 熟悉MyEclipse
  6. java正立三角形_java for循环练习(9*9乘法表、正三角形、菱形)
  7. 检查丢失的软件包并安装它们的优雅方法?
  8. 2021我的互联网秋招算法岗总结
  9. c语言编写记账程序,C语言会计记账管理系统
  10. 学科竞赛管理系统服务器错误,学科竞赛管理系统.docx
  11. Hamcrest 断言
  12. ldap统一用户认证php,Docker搭建OpenLDAP+phpLDAPadmin统一用户认证的方法
  13. 四川行无疆电商讲解拼多多电商产品销量如何清零
  14. 委托 和 事件 总括:
  15. Java实现 LeetCode第197场周赛 (题号5460,5461,5211,5463)
  16. 2018网易编程射击游戏
  17. 股票做手回忆录中的精华
  18. 信阳师范学院计算机老师,信阳师范学院计算机与信息技术学院导师教师师资介绍简介-郭华平...
  19. 生成器(generator)理解
  20. for 循环打印直角三角形、正三角形、棱形

热门文章

  1. Python MTCNN(人脸检测)项目附代码讲解(1)-原理与论文介绍
  2. mtcnn人脸检测python_基于MTCNN/TensorFlow实现人脸检测
  3. 西浦计算机研究生,西浦这个女生,竟然拒绝了哈佛研究生录取通知书
  4. 理想主义者–理查德.马修.斯托曼(GNU的传奇)
  5. 5G物理信道和物理信号定义
  6. Java操作redis数据库
  7. Kendo UI(一):Kendo UI Validator
  8. 小米汽车,是小米的大棋,也是小米的活棋
  9. hdu 3008 Warcraft
  10. 操作系统思维导图xmind