twemproxy原生使用的一致性hash算法:ketama,具体的算法设计如下: ketama是twemproxy使用的一种一致性hash算法,用来进行后端节点选择的一种策略,下来我们分析下它是如何工作的以及它的优缺点: 算法原理: ketama是使用md5签名的机制,主要使用hostname进行签名,然后从第16位进行截取,再生成一个32位无符号的数字作为hash值,对应的twemproxy实现是:
static uint32_t
ketama_hash(const char *key, size_t key_length, uint32_t alignment)
{
unsigned char results[16];
md5_signature((unsigned char*)key, key_length, results);
return ((uint32_t) (results[3 + alignment * 4] & 0xFF) << 24)
| ((uint32_t) (results[2 + alignment * 4] & 0xFF) << 16)
| ((uint32_t) (results[1 + alignment * 4] & 0xFF) << 8)
| (results[0 + alignment * 4] & 0xFF);
}
复制代码
说到这,我们不得不简单了解下连续区(continuum):
在,所有的md5值会分布在类似这个的圆环上,对应固定的hash值,根据hash值找到对应的server index 在ketama算法中,会在这个环上创建后端节点数 * 160个点,并均匀分布 下面是调用代码:
for (pointer_index = 1;
pointer_index <= pointer_per_server / pointer_per_hash;
pointer_index++) {
char host[KETAMA_MAX_HOSTLEN]= "";
size_t hostlen;
uint32_t x;
hostlen = snprintf(host, KETAMA_MAX_HOSTLEN, "%.*s-%u",
server->name.len, server->name.data,
pointer_index - 1);
for (x = 0; x < pointer_per_hash; x++) {
value = ketama_hash(host, hostlen, x);
pool->continuum[continuum_index].index = server_index;
pool->continuum[continuum_index++].value = value;
}
}
复制代码
每个服务节点分成160个点,hash值的key是host,每个server会有4个hash值,每个hash值有40个point 然后还是需要进行快速排序使它有序,这样就能尽可能的把server的分布打散 好了,到此,我们已经有了ketama的数据了,现在就是要看怎么用了,具体如下:
uint32_t
ketama_dispatch(struct continuum *continuum, uint32_t ncontinuum, uint32_t hash)
{
struct continuum *begin, *end, *left, *right, *middle;
ASSERT(continuum != NULL);
ASSERT(ncontinuum != 0);
begin = left = continuum;
end = right = continuum + ncontinuum;
while (left < right) {
middle = left + (right - left) / 2;
if (middle->value < hash) {
left = middle + 1;
} else {
right = middle;
}
}
if (right == end) {
right = begin;
}
return right->index;
}
复制代码
twemproxy工程中的使用要点: 1.根据要写入的数据key以及在配置文件中配置的hash算法例如crc16计算出key对应的hash值
2.调用ketama_dispatch()获取一个后端idx
3.在twemproxy的pool中根据idx拿到一个后端服务
4.update ketama的continuum记录,因为可能有节点失效了,需要根据配置文件中的fail_limit,auto_reject策略进行摘除
优势:一致性hash算法,即保证数据的尽量均匀有在有节点发生变动的时候只影响一个节点的数据,但是twemproxy在ketama中,并没有让ketama算法不计算失效节点,因为它的设计原则在这里是需要我们分布式系统自己做保障,让故障节点能尽快的恢复,否则不参与计算的话,可能会因为后续故障恢复后又一次进行可rehash,导致故障点到故恢复点之间写入的数据找不到了. 当然了,对于新增的节点,是要重新reash的.
不足: 强依赖分布式系统这些算法策略外部的因素进行可用性把控 其实在twemproxy当中,进行distoribution的算法还有很多,例如: MD5,fnv,jenkins等暂时未深究过....
现在为了支持按照槽位分片,我们使用hashslot算法,具体的算法设计如下:
hashslot的算法非常简单,基本上是在原有的ketama这些一致性hash算法上做了一个简单的槽位数求余来计算最终的那个continuum,核心流程一部分是上述说的ketama,最后只是加了一个:continuum= continuum + hash % 16384 其中的hash是crc系列算法计算出来的
简单了解下crc的原理: 这个人分析的不错,感兴趣的可以详细看看:blog.51cto.com/winda/10639… 个人对crc用在twemproxy中当做hash的理解就是:
1.使用crc是一种较好的算出少有冲突的hash值
2.crc也比较常见,算法成熟,性能也比较好,
在工程中的使用有点相同,都是需要update的,因为需要实时计算自己掌握的后端server的信息