2016 -Nginx的负载均衡 - 一致性哈希 (Consistent Hash)
Posted 允哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2016 -Nginx的负载均衡 - 一致性哈希 (Consistent Hash)相关的知识,希望对你有一定的参考价值。
nginx版本:1.9.1
算法介绍
当后端是缓存服务器时,经常使用一致性哈希算法来进行负载均衡。
使用一致性哈希的好处在于,增减集群的缓存服务器时,只有少量的缓存会失效,回源量较小。
在nginx+ats / haproxy+squid等CDN架构中,nginx/haproxy所使用的负载均衡算法便是一致性哈希。
我们举个例子来说明一致性哈希的好处。
假设后端集群包含三台缓存服务器,A、B、C。
请求r1、r2落在A上。
请求r3、r4落在B上。
请求r5、r6落在C上。
使用一致性哈希时,当缓存服务器B宕机时,r1/r2会仍然落在A上,r5/r6会仍然落在C上,
也就是说这两台服务器上的缓存都不会失效。r3/r4会被重新分配给A或者C,并产生回源。
使用其它算法,当缓存服务器B宕机时,r1/r2不再落在A上,r5/r6不再落在C上了。
也就是说A、B、C上的缓存都失效了,所有的请求都要回源。
这里不介绍一致性哈希算法的基本原理,如果不了解,先花个10分钟看下这篇文章:
http://www.codeproject.com/Articles/56138/Consistent-hashing
在分析模块代码之前,先来看下nginx所实现的一致性哈希算法。
1. 初始化upstream块
主要工作是创建和初始化真实节点、创建和初始化虚拟节点。
其中真实节点是使用round robin的方法创建的。
Q:总共有多少个虚拟节点,一个真实节点对应多少个虚拟节点?
累加真实节点的权重,算出总的权重值total_weight,虚拟节点的个数一般为total_weight * 160。
一个权重为weight的真实节点,对应的虚拟节点数为weight * 160。
Q:对于每一个真实节点,是如何创建其对应的虚拟节点的?
1. 真实节点的server成员是其server指令的第一个参数,首先把它解析为HOST和PORT。
base_hash = crc32(HOST 0 PORT)
一个真实节点对应weight * 160个虚拟节点,对于每个虚拟节点来说,base_hash都是一样的。
2. 为了使每个虚拟节点的hash值都不同,又引入了PREV_HASH,它是上一个虚拟节点的hash值。
hash = crc32(base_hash PREV_HASH)
3. 虚拟节点的server成员,指向真实节点的server成员。如此一来,通过比较虚拟节点和真实节点的
server成员是否相同,可以判断它们是否是相对应的。
创建和初始化好虚拟节点数组后,对其中的虚拟节点按照hash值进行排序,对于hash值相同的虚拟节点,只保留第一个。
经过上述步骤,我们得到一个所有虚拟节点组成的数组,其元素的hash值有序而不重复。也就是说,ring建立起来了。
2. 初始话请求的负载均衡数据
根据hash指令第一个参数的实时值KEY,KEY一般是$host$uri之类的,计算出本次请求的哈希值。
hash = crc32(KEY)
根据请求的哈希值,在虚拟节点数组中,找到“顺时针方向”最近的一个虚拟节点,其索引为i。
什么叫顺时针方向最近?就是point[i - 1].hash < hash <= point[i].hash。
本次请求就落在该虚拟节点上了,之后交由其对应的真实节点来处理。
3. 选取真实节点
在peer.init中,已经知道请求落在哪个虚拟节点上了。
在peer.get中,需要查找虚拟节点对应的真实节点。
根据虚拟节点的server成员,在真实节点数组中查找server成员相同的、可用的真实节点。
如果找不到,那么沿着顺时针方向,继续查找下一个虚拟节点对应的真实节点。
如果找到了一个,那么就是它了。
如果找到了多个,使用轮询的方法从中选取一个。
4. 缺陷和改进
一个虚拟节点和一个真实节点,是依据它们的server成员来关联的。
这会出现一种情况,一个虚拟节点对应了多个真实节点,因为:
如果server指令的第一个参数为域名,可能解析为多个真实节点,那么这些真实节点的server成员都是一样的。
对于一个请求,计算其KEY的hash值,顺时针找到最近的虚拟节点后,发现该虚拟节点对应了多个真实节点。
使用哪个真实节点呢?本模块就使用轮询的方法,来从多个真实节点中选一个。
但我们知道使用一致性哈希的场景中,真实节点一般是缓存服务器。
一个虚拟节点对应多个真实节点,会导致一个文件被缓存在多个缓存服务器上。
这会增加磁盘的使用量,以及回源量,显然不是我们希望看到的。
解决这个问题的方法其实很简单,就是虚拟节点和真实节点通过name成员来建立关联。
因为就算对应同一条server配置,server的第一个参数为域名,各个真实节点的name成员也是唯一的。
这样一来,找到了一个虚拟节点,就能找到一个唯一的真实节点,不会有上述问题了。
1. 真实节点
就是采用round robin算法所创建的后端服务器,类型为ngx_http_upstream_rr_peer_t。
需要注意的是,如果server指令的第一个参数是IP和端口,那么一条server指令只对应一个真实节点。
如果server指令的第一个参数是域名,一条server指令可能对应多个真实节点。
它们的server成员是相同的,可以通过name成员区分。