写点什么

架构师训练营 第五周 作业

用户头像
CR
关注
发布于: 2020 年 07 月 08 日



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

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



import java.util.*;
public class ConsistentHash {
/** 真实节点列表 */
private List<String> nodes = new ArrayList<>();
/** 每个物理节点对应虚拟节点个数 */
private int replicaCount = 1;
/** hash环 */
private SortedMap<Integer, String> circle = new TreeMap<>();
public ConsistentHash(List<String> nodes, int replicaCount) {
this.replicaCount = replicaCount;
for(String node : nodes) {
addNode(node);
}
}
// 增加节点
public void addNode(String node) {
for(int i = 0; i < replicaCount; i++) { // 使用虚拟节点计算hash值
circle.put(hash(node + "&" + i), node);
}
nodes.add(node);
}
public String getNode(String key) {
// 如果没有大于key的hash值,则取第一个节点;否则取大于key的hash值最近的一个节点
int hash = hash(key);
SortedMap<Integer, String> subMap = circle.tailMap(hash);
return subMap.isEmpty() ? circle.get(circle.firstKey()) : subMap.get(subMap.firstKey());
}
// hash值计算: FNV132 算法
private static int hash(String key) {
final int p = 16777619;
int hash = (int) 2166136261L;
for (int i = 0; i < key.length(); i++) {
hash = (hash ^ key.charAt(i)) * p;
}
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
return hash < 0 ? Math.abs(hash) : hash;
}
@Override
public String toString() {
return "ConsistentHash{" +
"nodes=" + nodes +
", replicaCount=" + replicaCount +
", circle=" + circle +
'}';
}
public static void main(String[] args) {
String[] servers = {"10.0.0.1:80", "10.0.0.2:80", "10.0.0.3:80", "10.0.0.4:80", "10.0.0.5:80",
"10.0.0.6:80", "10.0.0.7:80", "10.0.0.8:80", "10.0.0.9:80", "10.0.0.10:80"};
final int KV_COUNT = 1000000;
for(int i = 1; i <= 10000; i *= 10) { // 测试1,10,100,1000,10000个虚拟节点的标准差
test(servers, i, KV_COUNT);
}
}
private static void test(String[] servers, int replicaCount, int kvCount) {
ConsistentHash hash = new ConsistentHash(Arrays.asList(servers), replicaCount);
// System.out.println(hash);
Map<String, Integer> nodeCount = new HashMap<>();
for(int i = 0; i < kvCount; i++) {
String key = "test" + i;
String node = hash.getNode(key);
// 累计每个节点上的缓存个数
if (nodeCount.containsKey(node)) {
nodeCount.put(node, nodeCount.get(node) + 1);
} else {
nodeCount.put(node, 1);
}
}
System.out.println(nodeCount);
System.out.println(replicaCount + "个虚拟节点的标准差: " + standardDevition(nodeCount.values()));
}
public static double standardDevition(Collection<Integer> arrays) {
if (arrays.isEmpty()) {
return 0;
}
double sum = 0;
for(Integer i : arrays) {
sum += i;
}
double average = sum / arrays.size();
sum = 0;
for(Integer i : arrays) {
sum += ((double)i - average) * ((double)i - average);
}
return Math.sqrt(sum / arrays.size());
}
}



测试结果如下:

{10.0.0.5:80=326621, 10.0.0.1:80=49374, 10.0.0.10:80=2130, 10.0.0.8:80=64415, 10.0.0.3:80=155426, 10.0.0.2:80=127258, 10.0.0.9:80=123522, 10.0.0.4:80=921, 10.0.0.6:80=53350, 10.0.0.7:80=96983}

1个虚拟节点的标准差: 90075.0776830084

{10.0.0.5:80=115257, 10.0.0.1:80=171381, 10.0.0.10:80=87227, 10.0.0.8:80=52407, 10.0.0.3:80=105587, 10.0.0.2:80=110114, 10.0.0.9:80=68694, 10.0.0.4:80=74041, 10.0.0.6:80=89641, 10.0.0.7:80=125651}

10个虚拟节点的标准差: 32107.618958745603

{10.0.0.5:80=103440, 10.0.0.10:80=107766, 10.0.0.1:80=81795, 10.0.0.8:80=107464, 10.0.0.2:80=90577, 10.0.0.3:80=113026, 10.0.0.9:80=99807, 10.0.0.4:80=103692, 10.0.0.6:80=80738, 10.0.0.7:80=111695}

100个虚拟节点的标准差: 11131.994717929038

{10.0.0.5:80=100288, 10.0.0.10:80=97017, 10.0.0.1:80=101439, 10.0.0.8:80=102809, 10.0.0.3:80=96132, 10.0.0.2:80=100317, 10.0.0.9:80=100308, 10.0.0.4:80=104476, 10.0.0.7:80=97466, 10.0.0.6:80=99748}

1000个虚拟节点的标准差: 2462.0813958925078

{10.0.0.5:80=101560, 10.0.0.10:80=99464, 10.0.0.1:80=101221, 10.0.0.8:80=99891, 10.0.0.3:80=100201, 10.0.0.2:80=99912, 10.0.0.4:80=98405, 10.0.0.9:80=99259, 10.0.0.7:80=99851, 10.0.0.6:80=100236}

10000个虚拟节点的标准差: 862.7146689375346



用户头像

CR

关注

还未添加个人签名 2018.09.23 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营 第五周 作业