前提
路由策略有很多,轮询,随机,一致性hash,今天我们要分析的是是一致性hash,那么它有什么好处呢?
好处
- 可扩展
- 很好的容错性
缺点
- 数据倾斜问题: 可用虚拟节点解决
实现逻辑
- 将地址列表中每一个地址进行hash计算,存到TreeMap中,TreeMap会根据key进行排序,(key为hash,value为地址)
- 将jobId进行hash计算,得到hash值,然后在TreeMap中找到hash值最相近的hash值,得到对应的value,这个value就是我们请求要发送的地址。
- 当我们要扩容一个机器,那么TreeMap中就会多存一个数据,这个数据就是新扩容的机器地址,我们所说的一致性,并不是说新增之后所有的数据都能够正常访问,只是相对来说,节点越多,失效的数据越少,因为我们找节点(服务器ip地址)是按照范围来找的
代码
VIRTUAL_NODE_NUM 为虚拟节点号,可增加虚拟节点
private static int VIRTUAL_NODE_NUM = 5;
public String hashJob(int jobId, List<String> addressList) {
TreeMap<Long, String> addressRing = new TreeMap<Long, String>();
for (String address: addressList) {
for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {
long addressHash = hash("SHARD-" + address + "-NODE-" + i);
addressRing.put(addressHash, address);
}
}
long jobHash = hash(String.valueOf(jobId));
// 取到》=jobHash的hash值,该方法调用返回此映射,其键大于或等于fromKey的部分视图
SortedMap<Long, String> lastRing = addressRing.tailMap(jobHash);
if (!lastRing.isEmpty()) {
// 取到第一位的hash值
return lastRing.get(lastRing.firstKey());
}
return addressRing.firstEntry().getValue();
}
每次取得lastRing的第一个数据,作为我们分发请求地址
总结
可扩展:
如果我们新增和减少服务,对于已有的服务只需要重定位环空间中的一小部分数据即可
容错性
如服务器做缓存,服务器数量发生改变时,并不是所有都会失效,而只有部分缓存失效,前端的缓存仍然能分担整个系统的压力,而不至于所有压力都在同一个时间集中到后端服务器上。
如何解决数据倾斜
可增加虚拟节点,一个实际节点可以对应多个虚拟节点,如下图,30,20,50分别为3个地址,经过增加虚拟节点之后,可以增加为15个节点,虚拟节点越多,hash环上的节点就越多,缓存/请求均匀分布的概率就越大
解决了什么问题
均匀分布请求,并在服务器数量变化时,还能有很好的扩展性和容错性,解决了hash算法的由于服务器数量变化,引起所有缓存的失效,因为hash算法用到了取余,是固定的值,当服务器数量变化,那么就有可能取余的值都不一样,那么也就访问不到原来的服务器了,即所有缓存失效,如用一致性hash,那么值就变成了范围,而不是具体的值,那么这样就会大大减少缓存失效的影响