写点什么

架构师训练营 -week05- 作业

用户头像
大刘
关注
发布于: 2020 年 10 月 24 日
架构师训练营 -week05- 作业

作业:



  1. 用你熟悉的编程语言实现一致性 hash 算法。

  2. 编写测试用例测试这个算法,测试 100 万 KV 数据,10 个服务器节点的情况下,计算这些 KV 数据在服务器上分布数量的标准差,以评估算法的存储负载不均衡性。



参考了一些网上的实现方案,主要思路和代码如下:

基本思路:

  1. 造一个hash环

  2. 在哈希环上映射服务器节点

  3. 增加虚拟节点,分布在hash环上

  4. 路由找到对应的节点



主要代码:

//使用FNV1_32_HASH算法计算服务器的Hash值,也可以用MD5或者其他方式实现
public static long hash(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 void addNode(Node pNode, int vNodeCount) {
if (vNodeCount < 0) throw new IllegalArgumentException("illegal virtual node counts :" + vNodeCount);
int existingReplicas = getExistingReplicas(pNode);
for (int i = 0; i < vNodeCount; i++) {
VirtualNode vNode = new VirtualNode(pNode, i + existingReplicas);
ring.put(hashFunction.hash(vNode.getKey()), vNode);
}
}



//根据IP地址,找到对应的Node。objectKey可以理解为IP地址+端口
public Node routeNode(String objectKey) {
if (ring.isEmpty()) {
return null;
}
Long hashVal = hashFunction.hash(objectKey);
//得到大于该Hash值的所有Map
SortedMap<Long,VirtualNode> tailMap = ring.tailMap(hashVal);
//如果能找到最近的node就返回这个node,如果找不到就去这个ring上的第一个node
Long nodeHashVal = !tailMap.isEmpty() ? tailMap.firstKey() : ring.firstKey();
return ring.get(nodeHashVal).getPhysicalNode();
}



计算标准差:

初始化了10个Node(ip地址+端口),当虚拟节点(VirtualNode)值为1的时候,计算的结果偏差比较大,当VirtualNode=10的时候,就变得较为均衡了。

VirtualNode = 1

{10.10.9.210-8080=995, 10.10.9.210-8081=7378, 10.8.1.11-8080=15805, 10.8.1.11-8081=62, 10.8.1.11-8082=3894, 10.8.3.99-8080=31893, 10.8.3.99-8081=18163, 10.8.3.99-8082=17097, 10.9.11.105-8080=3042, 10.9.11.105-8081=1671}



VirtualNode = 10

{10.10.9.210-8080=10545, 10.10.9.210-8081=9479, 10.8.1.11-8080=6283, 10.8.1.11-8081=9143, 10.8.1.11-8082=11848, 10.8.3.99-8080=9668, 10.8.3.99-8081=10147, 10.8.3.99-8082=10693, 10.9.11.105-8080=15840, 10.9.11.105-8081=6354}



VirtualNode = 100

{10.10.9.210-8080=10077, 10.10.9.210-8081=10599, 10.8.1.11-8080=11064, 10.8.1.11-8081=9359, 10.8.1.11-8082=9086, 10.8.3.99-8080=9936, 10.8.3.99-8081=9750, 10.8.3.99-8082=11025, 10.9.11.105-8080=8476, 10.9.11.105-8081=10628}



主要代码:

//初始化10个IP,并且设置为100个虚拟节点
ConsistentHashRouter consistentHashRouter = new ConsistentHashRouter(
Arrays.asList(node1, node2, node3, node4, node5, node6, node7, node8, node9, node10),
100);//10 virtual node
//初始化10w个随机IP地址,加入到arraylist里
List<String> requestIps = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
requestIps.add(getRandomIp());
}
//把10w个kw,分配到10 node * 100 virtual node里,存放为treemap:key是node值,value是分配到这个key上的数量
System.out.println(goRoute(consistentHashRouter, requestIps.toArray(new String[requestIps.size()])).toString());



//把10w个随机IP地址,找到对应的Node加入进去,并且更新每一个Node对应的数量值。
private static TreeMap<String, AtomicInteger> goRoute(ConsistentHashRouter consistentHashRouter, String... requestIps) {
TreeMap<String, AtomicInteger> res = new TreeMap<>();
for (String requestIp : requestIps) {
Node mynode = consistentHashRouter.routeNode(requestIp);
res.putIfAbsent(mynode.getKey(), new AtomicInteger());
res.get(mynode.getKey()).incrementAndGet();
// System.out.println(requestIp + " is routed to. " + mynode.getKey());
}
return res;
}



代码地址:

https://github.com/liuqi1024/geektime-arch-train/tree/main/week05

用户头像

大刘

关注

大道至简,知易行难 2017.12.27 加入

想成为合格架构师的架构师

评论

发布
暂无评论
架构师训练营 -week05- 作业