1
第五周作业
发布于: 2020 年 11 月 23 日
用你熟悉的编程语言实现一致性 hash 算法。
算法接口:
public interface ConsistencyHash {
/** * 设置虚拟节点数量 * @param num */ void setVirtualNodeNum(int num);
/** * 添加服务器节点 * @param service */ void addService(String service);
/** * 批量添加 * @param services */ void addServices(String[] services);
/** * 获取顺时针最近的服务器节点 * @param key * @return */ String getService(Object key);
}复制代码
算法的具体实现:
public class ConsistencyHashImpl implements ConsistencyHash {
private TreeMap<Integer, String> hashServices = new TreeMap<>();
private int virtualNodeNum = 0;
public static final String VIRTUAL_NODE_SEG = "V";
@Override public void setVirtualNodeNum(int num) { this.virtualNodeNum = num; }
@Override public void addService(String service) { int hashcode = getHash(service); hashServices.put(hashcode, service);
addVirtualNode(service); }
private void addVirtualNode(String service) { for (int i = 0; i < virtualNodeNum; i++) { int hashcode = getHash(service + VIRTUAL_NODE_SEG + i);
hashServices.put(hashcode, service); } }
@Override public void addServices(String[] services) { Stream.of(services).forEach(this::addService); }
@Override @Nullable public String getService(Object key) { if(hashServices.isEmpty()) { return null; } int hashcode = getHash(key);
String service = "";
// 大于hashcode的数据,顺时针取第一个 SortedMap sortedMap = hashServices.tailMap(hashcode); if (sortedMap != null && !sortedMap.isEmpty()) { service = hashServices.get(sortedMap.firstKey()); } else { service = hashServices.get(hashServices.firstKey()); } if(service.contains(VIRTUAL_NODE_SEG)) { service = service.split(VIRTUAL_NODE_SEG)[0]; } return service; }
private int getHash(Object obj){ String str = obj.toString(); 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 class Test {
public static void main(String[] args) {
// 服务器节点 String[] services = {"127.0.0.1:8080", "127.0.0.2:8080", "127.0.0.3:8080", "127.0.0.4:8080", "127.0.0.5:8080", "127.0.0.6:8080", "127.0.0.7:8080", "127.0.0.8:8080", "127.0.0.9:8080", "127.0.0.10:8080"};
// 记录每个服务器存储的数量 Map<String, Integer> count = new HashMap<>();
// 一致性hash算法 ConsistencyHash consistencyHash = new ConsistencyHashImpl(); consistencyHash.setVirtualNodeNum(150); consistencyHash.addServices(services);
// 随机数key, Random random = new Random(); for (int i = 0; i < 1000000; i++) { int data = random.nextInt(); String service = consistencyHash.getService(data);
if (count.containsKey(service)) { count.put(service, (count.get(service) + 1)); } else { count.put(service, 1); } }
System.out.println(count); }
}复制代码
执行结果:
编写测试用例测试这个算法,测试 100 万 KV 数据,10 个服务器节点的情况下,计算这些 KV 数据在服务器上分布数量的标准差,以评估算法的存储负载不均衡性。
标准差计算:
//标准差σ=sqrt(s^2) public static double standardDiviation(int[] x) { int m=x.length; double sum=0; for(int i=0;i<m;i++){//求和 sum+=x[i]; } double dAve=sum/m;//求平均值 double dVar=0; for(int i=0;i<m;i++){//求方差 dVar+=(x[i]-dAve)*(x[i]-dAve); } // 平均方差开平方 return Math.sqrt(dVar/m); }复制代码
当虚拟节点为 150 时,标准差为:4358.570338998787
当虚拟节点为 1500 时,标准差为:2757.541114834011
当虚拟节点为 15000 时,标准差为:993.5307745611104
当虚拟节点为 150000 时,标准差为:263.4145022583229
划线
评论
复制
发布于: 2020 年 11 月 23 日阅读数: 43
jizhi7
关注
还未添加个人签名 2018.09.08 加入
还未添加个人简介











评论