用你熟悉的编程语言实现一致性 hash 算法。
编写测试用例测试这个算法,测试 100 万 KV 数据,10 个服务器节点的情况下,计算这些 KV 数据在服务器上分布数量的标准差,以评估算法的存储负载不均衡性。
package com.example.design.hash;import org.springframework.util.StringUtils;import java.util.LinkedList;import java.util.List;import java.util.SortedMap;import java.util.TreeMap;/*** 带虚拟节点的一致性Hash算法*/public class ConsistentHashingWithVirtualNode {/*** 待添加入Hash环的服务器列表*/public static String[] servers ={"192.168.0.0:111", "192.168.0.1:111", "192.168.0.2:111", "192.168.0.3:111", "192.168.0.4:111","192.168.0.5:111", "192.168.0.6:111", "192.168.0.7:111", "192.168.0.8:111", "192.168.0.9:111"};private static List<String> realNodes = new LinkedList<>();private static SortedMap<Integer, String> virtualNodes = new TreeMap<>();/*** /虚拟节点的数目, 一个真实节点对应的虚拟节点数*/private static final int VIRTUAL_NODES = 100;static {//先把原始的服务器添加到真实结点列表中for (int i = 0; i < servers.length; i++) {realNodes.add(servers[i]);}//再添加虚拟节点,遍历LinkedList使用foreach循环效率会比较高for (String str : realNodes) {for (int i = 0; i < VIRTUAL_NODES; i++) {String virtualNodeName = str + "&&VN" + String.valueOf(i);int hash = getHash(virtualNodeName);// System.out.println("虚拟节点[" + virtualNodeName + "]被添加, hash值为" + hash);virtualNodes.put(hash, virtualNodeName);}}System.out.println();}/*** 使用FNV1_32_HASH算法计算服务器的Hash值* @param str* @return*/public 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 key* @return*/public static String getServer(String key) {//得到该key的hash值int hash = getHash(key);// 得到大于该Hash值的所有MapSortedMap<Integer, String> subMap = virtualNodes.tailMap(hash);String virtualNode;if (subMap.isEmpty()) {//如果没有比该key的hash值大的,则从第一个node开始Integer i = virtualNodes.firstKey();//返回对应的服务器virtualNode = virtualNodes.get(i);} else {//第一个Key就是顺时针过去离node最近的那个结点Integer i = subMap.firstKey();//返回对应的服务器virtualNode = subMap.get(i);}//virtualNode虚拟节点名称要截取一下if (!StringUtils.isEmpty(virtualNode)) {return virtualNode.substring(0, virtualNode.indexOf("&&"));}return null;}}
package com.example.design.hash;import java.util.Arrays;import java.util.Map;import java.util.TreeMap;/*** @description: 测试 100 万 KV 数据,10 个服务器节点的情况下,* 计算这些 KV 数据在服务器上分布数量的标准差,以评估算法的存储负载不均衡性。* @author: jcwang* @create: 2020-07-08 10:00**/public class Test {/*** 计算标准方差** @param array* @return*/public static double calcStd(Integer[] array) {double avg = Arrays.stream(array).mapToInt(Integer::intValue).average().orElse(0d);double avgStd =Arrays.stream(array).map(count -> Math.pow(count - avg, 2)).mapToDouble(Double::doubleValue).average().orElse(0d);return Math.sqrt(avgStd);}public static void main(String[] args) {int keyNum = 10000;Map<String, Integer> serverCount = new TreeMap<>();for (String server : ConsistentHashingWithVirtualNode.servers) {serverCount.put(server, 0);}String serverName = "";String key = "";for (int i = 0; i < keyNum; i++) {key = "key" + System.currentTimeMillis();serverName = ConsistentHashingWithVirtualNode.getServer(key);serverCount.put(serverName, serverCount.get(serverName) + 1);}Integer[] array = new Integer[serverCount.size()];double rs = calcStd(serverCount.values().toArray(array));System.out.println("rs = " + rs);}}
作业提交地址: https://jinshuju.net/f/0zLWY4
