第五周作业
发布于: 2020 年 07 月 08 日
用你熟悉的编程语言实现一致性 hash 算法。
编写测试用例测试这个算法,测试 100 万 KV 数据,10 个服务器节点的情况下,计算这些 KV 数据在服务器上分布数量的标准差,以评估算法的存储负载不均衡性。
源码
ConsistentHashing.java
哈希环的实现,使用实现红黑树的TreeMap保存虚拟节点
import java.util.HashMap;import java.util.Map;import java.util.TreeMap;public class ConsistentHashing { /** * 服务器信息集合,key:服务器hash值,value:服务信息(简单用字符串代替) */ private Map<Integer, String> serverMap = new HashMap<>(); /** * hash环,存储了环上所有虚拟节点,key:虚拟节点hash值,value:服务器hash值 */ private TreeMap<Integer, Integer> hashRing = new TreeMap<>(); /** * 每个服务对应虚拟节点数 */ private int nodeCount; public ConsistentHashing(int nodeCount) { if (nodeCount <= 0) { //至少有服务本身节点 this.nodeCount = 1; return; } this.nodeCount = nodeCount; } /** * 增加服务 * * @param serverId 服务标识 */ public void appendServer(String serverId) { int serverHash = HashCode.get(serverId); if (!serverMap.containsKey(serverHash)) { serverMap.put(serverHash, serverId); //添加虚拟节点 for (int i = 0; i < nodeCount; i++) { hashRing.put(HashCode.get(serverId + ":VIR" + i), serverHash); } } } /** * 移除服务 * * @param serverId 服务标识 */ public void removeServer(String serverId) { for (int i = 0; i < nodeCount; i++) { hashRing.remove(HashCode.get(serverId + ":VIR" + i)); } } /** * 获取服务信息 * * @param requestKey 请求标识 * @return 服务信息 */ public String getServer(String requestKey) { if (serverMap.isEmpty()) { return null; } Map.Entry<Integer, Integer> targetNode = hashRing.ceilingEntry(HashCode.get(requestKey)); if (targetNode == null) { targetNode = hashRing.firstEntry(); } return serverMap.get(targetNode.getValue()); }}
HashCode.java
用于获取hash值,采用FNV算法
public class HashCode { public static int get(String data){ return FNVHash1(data); } private static int FNVHash1(String data) { final int p = 16777619; int hash = (int)2166136261L; for(int i=0;i<data.length();i++) { hash = (hash ^ data.charAt(i)) * p; } hash += hash << 13; hash ^= hash >> 7; hash += hash << 3; hash ^= hash >> 17; hash += hash << 5; return hash; }}
StandardDeviation.java
标准差计算类
import java.util.Collection;public class StandardDeviation { public static double math(Collection<Integer> datas){ //求出数组的总和 double average = datas.stream().mapToInt(Integer::intValue).average().getAsDouble(); //求出方差 double var = datas.stream().map(d->(d-average)*(d-average)).mapToDouble(Double::doubleValue).sum(); //求出标准差 return Math.sqrt(var/datas.size()); }}
统计结果
可以看到,在虚拟节点在600左右达到最好的效果
划线
评论
复制
发布于: 2020 年 07 月 08 日阅读数: 45
CP
关注
还未添加个人签名 2018.03.15 加入
还未添加个人简介
评论