第 5 周结构师训练营——作业
发布于: 2020 年 07 月 07 日
用你熟悉的编程语言实现一致性 hash 算法。
编写测试用例测试这个算法,测试 100 万 KV 数据,10 个服务器节点的情况下,计算这些 KV 数据在服务器上分布数量的标准差,以评估算法的存储负载不均衡性。
(1)不使用虚拟节点
public class ConsistentHashWithoutVirtualNode { private static String[] servers = {"192.168.0.1:8090","192.168.0.2:8090","192.168.0.3:8090","192.168.0.4:8090", "192.168.0.5:80990","192.168.0.6:8090","192.168.0.7:8090","192.168.0.8:8090","192.168.0.9:8090","192.168.0.10:8090"}; //key 表示服务器hash值,value表示服务器 private static SortedMap<Integer,String> sortedMap = new TreeMap<Integer,String>(); //sortedMap表示闭型环,初始化将服务器加入到环中 static { for (String server : servers) { int hash = getHash(server); sortedMap.put(hash, server); System.out.println("服务器[" + server + "]加入集合中,对应的hash值为:" + hash); } } /** * 获取数据存放的服务器的key * @param str */ private static Integer getserver(String str) { int hash = getHash(str); SortedMap<Integer,String> resultMap = sortedMap.tailMap(hash); if(null == resultMap || resultMap.isEmpty()) return sortedMap.firstKey(); else return resultMap.firstKey(); } /** * 获取key的hash值 * @param key * @return */ private 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; } /** * 生成随机字符串 * @param len * @return */ public static String generatedString(int len) { byte[] array = new byte[len]; new Random().nextBytes(array); String str = new String(array,Charset.forName("UTF-8")); return str; } public static void main(String[] args) { Map<Integer,Integer> countMap = new HashMap<Integer,Integer>(); for(int i=1;i<=1000000;i++) { String str = generatedString(8); Integer hash = getserver(str); if(countMap.containsKey(hash)) countMap.put(hash, countMap.get(hash)+1); else countMap.put(hash, 1); } double sum = 0; for(Integer hash : sortedMap.keySet()) { sum += Math.pow(countMap.get(hash)-100000,2); System.out.println("服务器["+sortedMap.get(hash)+"]存储数据量为:"+countMap.get(hash)+"占比为:"+(countMap.get(hash)/10000)+"%"); } System.out.println("标准差为:"+(Math.sqrt(sum/10))); }}
输出结果
(2)使用虚拟节点
public class ConsistentHashUseVirtualNode { //服务器列表 private static String[] servers = {"192.168.0.1:8090","192.168.0.2:8090","192.168.0.3:8090","192.168.0.4:8090", "192.168.0.5:80990","192.168.0.6:8090","192.168.0.7:8090","192.168.0.8:8090","192.168.0.9:8090","192.168.0.10:8090"}; //便于生产上伸缩机器,使用LinkedList 存放服务器 private static List<String> realNode = new LinkedList<String>(); //虚拟节点,key表示hash value对应虚拟节点名称 private static SortedMap<Integer,String> virtualNode = new TreeMap<Integer,String>(); //设置虚拟节点数 private static final int vnode = 100; static{ //将服务器加入到真实节点列表中 for(String str : servers) { realNode.add(str); //创建虚拟节点 for(int i = 1;i <= vnode ;i++) { String serverName = str + "&&VN" + i; int hash = getHash(serverName); virtualNode.put(hash, serverName); } } } /** * 获取数据存储服务器 * @param str * @return */ private static String getServer(String str) { int hash = getHash(str); String nodeName = null; SortedMap<Integer,String> subMap = virtualNode.tailMap(hash); if(null == subMap || subMap.isEmpty()) { nodeName = virtualNode.get(virtualNode.firstKey()); return nodeName.substring(0, nodeName.indexOf("&&")); } nodeName = subMap.get(subMap.firstKey()); return nodeName.substring(0, nodeName.indexOf("&&")); } /** * 获取hash值 * @param str * @return */ private static Integer 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; } /** * 生成随机字符串 * @param len * @return */ public static String generatedString(int len) { byte[] array = new byte[len]; new Random().nextBytes(array); String str = new String(array,Charset.forName("UTF-8")); return str; } public static void main(String[] args) { System.out.println("虚拟节点为: "+vnode+" 的结果为:"); Map<String,Integer> resultMap = new HashMap<String,Integer>(); double sum = 0; for(int i = 1; i <= 1000000; i++) { String str = generatedString(8); String server = getServer(str); if(resultMap.containsKey(server)) resultMap.put(server, resultMap.get(server)+1); else resultMap.put(server, 1); } for(String name : resultMap.keySet()) { sum += Math.pow((resultMap.get(name)-100000), 2); System.out.println("服务器[" + name + "]存储数据量为:" + resultMap.get(name)+"占比为:"+(resultMap.get(name)/10000)+"%"); } System.out.println("标准差为:"+Math.sqrt(sum/10)); }}
分别对虚拟节点数设为10,100,200,300,500场景做了验证,结果如下:
随着虚拟节点数的增加,数据分布率逐渐均匀,标准差逐渐减小;
划线
评论
复制
发布于: 2020 年 07 月 07 日阅读数: 48
jiangnanage
关注
还未添加个人签名 2019.04.11 加入
还未添加个人简介
评论