一致性hash算法
本文于
1780
天之前发表,文中内容可能已经过时。
主要用途
相比简单hash 减少分布式缓存 添加一台机器或者减少一台机器时候,对于缓存路由到新的机子,导致缓存穿透的问题。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
| /** * 一致性hash算法 */ public class ConsistenceHash { /** * 物理节点 */ private List<String> realNode = new ArrayList<String>(); /** * 物理节点对应的虚拟节点。key 对应的是物理节点,value对应的是虚拟节点的hash值 */ private Map<String, List<Integer>> real2VirtualMap = new HashMap(); /** * 虚拟节点数量 */ private int virtualNum = 100; /** * 虚拟节点对应的物理节点,利用红黑树存贮 */ private SortedMap<Integer, String> sortedMap = new TreeMap<Integer, String>();
/** * 添加物理节点 * * @param node */ public void addServer(String node) { realNode.add(node); String vnode = null; int count = 0, i = 0; while (count < virtualNum) { vnode = node + "V_" + i;//生成虚拟节点 int hashValue = getHash(vnode); // 虚拟节点hash value if (!sortedMap.containsKey(hashValue)) { sortedMap.put(hashValue, node); if (!real2VirtualMap.containsKey(node)) { List<Integer> virtualNodes = new ArrayList<Integer>(); real2VirtualMap.put(node, virtualNodes); } real2VirtualMap.get(node).add(hashValue); sortedMap.put(hashValue, node); } count++; } }
/** * 删除物理节点,当节点下线或者说节点被移除 * * @param node */ public void removeServer(String node) { // 虚拟节点下园环 List<Integer> virtualNodes = real2VirtualMap.get(node); if (virtualNodes != null && !virtualNodes.isEmpty()) { for (Integer virtualNode : virtualNodes) { sortedMap.remove(virtualNode); } } //删除物理节点 realNode.remove(node); virtualNodes.remove(node);
}
/** * 通过 缓存key 获取物理节点 * * @param key * @return */ public String getServer(String key) { int hashValue = getHash(key); SortedMap<Integer, String> subMap = sortedMap.tailMap(hashValue); //圆环当没有比这个hash更大的值的时候,获取最小值,最大值的总结是最小值 if (subMap == null || subMap.isEmpty()) { return sortedMap.get(sortedMap.firstKey()); } return subMap.get(subMap.firstKey()); }
/** * 为什么不用 object的hash,因为 自带的hash 散列层度不够,而且有可能是负数 * * @param str * @return */ private static int getHash(String str) { final int p = 1677619; 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 static void main(String[] args){ ConsistenceHash consistenceHash=new ConsistenceHash(); consistenceHash.addServer("192.168.1.1"); consistenceHash.addServer("192.168.1.2"); consistenceHash.addServer("192.168.1.3"); consistenceHash.addServer("192.168.1.4"); consistenceHash.addServer("192.168.1.5"); System.out.println(consistenceHash.getServer("1")); System.out.println(consistenceHash.getServer("2")); System.out.println(consistenceHash.getServer("3")); System.out.println(consistenceHash.getServer("4")); System.out.println(consistenceHash.getServer("5")); consistenceHash.removeServer("192.168.1.5"); System.out.println("after remove "+consistenceHash.getServer("1")); System.out.println("after remove "+consistenceHash.getServer("2")); System.out.println("after remove "+consistenceHash.getServer("3")); System.out.println("after remove "+consistenceHash.getServer("4")); System.out.println("after remove "+consistenceHash.getServer("5")); } }
|
参考资料