在负载均衡的策略中,一般有:
- 轮询
- 权重
- 最小活跃
- 一致性hash
场景
Dubbo中的负载均衡策略就有一致性hash,那一致性hash或者说hash环到底是什么呢?
假设我有多台缓存服务器,编号分别为0,1,2…n-1。对于一个key,由客户端来决定存放到哪台机器,那最简单的hash公式就是 key % N,其中N是机器的总数。
但这有个问题,一旦机器数变少,或者增加机器,N发生变化,那之前存放的数据就全部无效了。因为你按照新的N值取模计算出的机器编号,和当时按旧的N值取模算出的机器编号肯定是不等的,也就意味着绝大部分缓存会失效。
比如原先N是5,key是8,这个key会在编号3(8%5 = 3)的机器上,如果说编号5的机器挂了,这时N变成4,key是8,这个key会在编号0(8%4 = 0)的机器上,这样就不对。
会引起两个问题:
- 缓存的位置变化,找不到缓存。
- 缓存雪崩,因为很多缓存都失效了。
这个问题的解决办法就是用1种特别的Hash函数,尽可能使得,增加机器/减少机器时,缓存失效的数目降到最低,这就是Hash环,或者叫一致性Hash。
一致性hash的做法
上面说的Hash函数,只经过了1次hash,即把key hash到对应的机器编号。
而Hash环有2次Hash,就是对2的32次方取模:
- 把所有机器编号hash到这个环上
- 把key也hash到这个环上。然后在这个环上进行匹配,看这个key和哪台机器匹配。
假设有4台服务器,地址为ip1,ip2,ip3,ip4。
服务器IP哈希
首先计算四个ip地址对应的hash值
hash(ip1),hash(ip2),hash(ip3),hash(ip3),计算出来的hash值是0到2的32次方-1中的一个值,这四个值在一致性hash环上呈现如下图:
用户请求的key哈希
根据hash(用户key)计算路由规则(对2的32次方取模后的hash值),然后看hash值落到了hash环的那个地方,根据hash值在hash环上的位置顺时针找距离最近的ip作为路由ip。
如上图可知user1,user2的请求会落到服务器ip2进行处理,User3的请求会落到服务器ip3进行处理,user4的请求会落到服务器ip4进行处理,user5,user6的请求会落到服务器ip1进行处理。
有服务器挂掉怎么办
下面考虑当ip2的服务器挂了的时候会出现什么情况?
当ip2的服务器挂了的时候,一致性hash环大致如下图:
根据顺时针规则可知user1,user2的请求会被服务器ip3进行处理,而其它用户的请求对应的处理服务器不变,也就是只有之前被ip2处理的一部分用户的映射关系被破坏了,并且其负责处理的请求被顺时针下一个节点委托处理。
有新增服务器怎么办
当新增一个ip5的服务器后,一致性hash环大致如下图:
根据顺时针规则可知之前user5的请求应该被ip5服务器处理,现在被新增的ip5服务器处理,其他用户的请求处理服务器不变,也就是新增的服务器顺时针最近的服务器的一部分请求会被新增的服务器所替代。
一致性hash倾斜的问题
理想中服务器的IP是均匀分布在环中,但是现实并非如此,如果出现下面不均匀的情况,每台服务器上收到的key就不均匀,不能保证每个服务器处理的请求的数量大致相同。
虚拟节点
当服务器节点比较少的时候会出现上面所说的一致性hash倾斜的问题,一个解决方法是多加机器,但是加机器是有成本的,那么就加虚拟节点,比如上面三个机器,每个机器引入1个虚拟节点后的一致性hash环的图如下:
其中ip1-1是ip1的虚拟节点,ip2-1是ip2的虚拟节点,ip3-1是ip3的虚拟节点。
可知当物理机器数目为M,虚拟节点为N的时候,实际hash环上节点个数为M*N。比如当客户端计算的hash值处于ip2和ip3或者处于ip2-1和ip3-1之间时候使用ip3服务器进行处理。
均匀一致哈希
上节我们使用虚拟节点后的图看起来比较均衡,但是如果生成虚拟节点的算法不够好很可能会得到下面的环:
可知每个服务节点引入1个虚拟节点后,情况相比没有引入前均衡性有所改善,但是并不均衡。
均衡的一致性hash应该是如下图:
均匀一致性hash的目标是如果服务器有N台,客户端的hash值有M个,那么每个服务器应该处理大概M/N个用户的。也就是每台服务器负载尽量均衡。
链接:https://www.jianshu.com/p/e968c081f563