1
训练营第五周作业
发布于: 2020 年 11 月 22 日
用你熟悉的编程语言实现一致性 hash 算法
public class ConsistencyShardingVirtualNodeDemo {
/** 集群地址列表 */
private static String[] groups = {
"192.168.0.0:8080",
"192.168.0.1:8080",
"192.168.0.2:8080",
"192.168.0.3:8080"};
/** 虚拟节点映射关系 */
private static SortedMap<Integer, String> virtualNodes = new TreeMap<>();
private static final int VIRTUAL_NODE_NUM = 1000;
static {
// 将虚拟节点映射到Hash环上
for (int i = 0; i < groups.length; i++) {
String group = groups[i];
for (int j = 0; j < VIRTUAL_NODE_NUM; j++) {
String virtualNodeName = getVirtualNodeName(group, j);
int hash = getHash(virtualNodeName);
virtualNodes.put(hash, virtualNodeName);
}
}
}
private static String getVirtualNodeName(String realName, int num) {
return realName + "&VN-" + String.valueOf(num);
}
private static String getRealNodeName(String virtualName) {
return virtualName.split("&")[0];
}
private static String getServer(String widgetKey) {
int hash = getHash(widgetKey);
// 只取出所有大于该hash值的部分而不必遍历整个Tree
SortedMap<Integer, String> subMap = virtualNodes.tailMap(hash);
String virtualNodeName;
if (subMap == null || subMap.isEmpty()) {
// hash值在最尾部,应该映射到第一个group上
virtualNodeName = virtualNodes.get(virtualNodes.firstKey());
} else {
virtualNodeName = subMap.get(subMap.firstKey());
}
return getRealNodeName(virtualNodeName);
}
private static void randomTest() {
Map<String, Integer> resMap = new HashMap<>();
for (int i = 0; i < 1000000; i++) {
String server = getServer(UUID.randomUUID().toString());
if (resMap.containsKey(server)) {
resMap.put(server, resMap.get(server) + 1);
} else {
resMap.put(server, 1);
}
}
resMap.forEach((key, value) -> {
System.out.println("server: " + key + " count: " + value + "(" + value / 10000.0D + "%)");
});
}
public static int getHash(String str) {
final int p = 16777619;
int hash = (int) 2166136261L;
for (int i = 0; i < str.length(); i++) {
hash = (hash ^ str.charAt(i)) * p;
}
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
if (hash < 0) {
hash = Math.abs(hash);
}
return hash;
}
public static void main(String[] args) {
randomTest();
}
}
复制代码
这个算法并没有第一次想出来,主要是卡在虚拟节点,不知道怎么加入。后来参考了一些网上的实现,发现是我想的复杂了。本质上,是对于 String 的 Hash,只要构造可以溯源的字符串就可以了(文中中的`$VN-`)。
另外,对于这个算法来说,`getHash`方法 是比较重要的一个点,如果算出来的 hash 不能均匀的分布在圆上,那么缓存也分布也不会均匀。
划线
评论
复制
发布于: 2020 年 11 月 22 日阅读数: 32
大脸猫
关注
还未添加个人签名 2018.04.27 加入
还未添加个人简介
评论 (1 条评论)