hash一致性java实现

2019-04-13 17:04发布

简单的用代码表述一下hash一致性 package com; import java.util.SortedMap; import java.util.TreeMap; /** * hash一致性算法 * 存在雪崩的情况,所以我们创建多个虚拟节点对应物理机可以利用虚拟节点 * Created by lijianzhen1 on 2017/9/6. */ public class HashIdentical { //待添加入Hash环的服务器列表 protected 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"}; //key表示服务器的hash值,value表示服务器 private static SortedMap sortedMap = new TreeMap(); //程序初始化,将所有的服务器放入sortedMap中 static { for (int i = 0; i < servers.length; i++) { int hash = getHash(servers[i]); //System.out.println("[" + servers[i] + "]加入集合中, 其Hash值为" + hash); sortedMap.put(hash, servers[i]); } System.out.println(); } //使用FNV1_32_HASH算法计算服务器的Hash值,这里不使用重写hashCode的方法,最终效果没区别 protected static int getHash(String server) { final int p = 16777619; int hash = (int) 2166136261L; for (int i = 0; i < server.length(); i++) hash = (hash ^ server.charAt(i)) * p; //以下操作注意尽量使用质数 hash += hash << 5; hash ^= hash >> 3; hash += hash << 11; hash ^= hash >> 17; hash += hash << 19; // 如果算出来的值为负数则取其绝对值 if (hash < 0) hash = Math.abs(hash); return hash; } //获得当前路由的节点 private static String getServer(String key) { //得到对应的hash值 int hash = getHash(key); //得到大于hash值中所有map SortedMap subMap = sortedMap.tailMap(hash); //注意下边实现了hash一致环 if (subMap.isEmpty()) { //如果当前的值没有大于该值的,从第一个节点开始 Integer i = sortedMap.firstKey(); //返回对应的服务器地址 return sortedMap.get(i); } else { //顺时针下个节点位置,离当前hash值最近的那个位置 Integer i = subMap.firstKey(); return subMap.get(i); } } public static void main(String[] args) { String[] keys = {"美国", "俄罗斯", "中国"}; for (String key : keys) System.out.println(key + "-的hash值为" + getHash(key) + ",被路由到节点" + getServer(key)); } }
package com; import org.springframework.util.StringUtils; import java.util.*; /** * hash一致性算法 * 存在雪崩的情况,所以我们创建多个虚拟节点对应物理机可以利用虚拟节点 * 为了分布均匀,创建虚拟节点 * Created by lijianzhen1 on 2017/9/6. */ public class VirtualHashIdentical extends HashIdentical { //真实结点列表,考虑到服务器上线、下线的场景,即添加、删除的场景会比较频繁,这里使用LinkedList会更好 private static List realNodes = new LinkedList(); //虚拟节点,key表示虚拟节点的hash值,value表示虚拟节点的名称 private static SortedMap virtualNodes = new TreeMap(); //虚拟节点的数目,这里写死,为了演示需要,一个真实结点对应5个虚拟节点 private static final int VIRTUAL_NODES = 5; static { //先把原始的服务器添加到真实结点列表中 for (int i = 0; i < servers.length; i++) realNodes.add(servers[i]); //再添加虚拟节点,遍历LinkedList使用foreach循环效率会比较高 for (String str : realNodes) { virtualNodes.putAll(generateVitureNode(str)); } System.out.println(); } /** * 移除一致性hash环中的真实节点 */ private static void delTrueNode(String server) { //移除虚拟节点 SortedMap rmVitureNode = generateVitureNode(server); for (Iterator> entry = rmVitureNode.entrySet().iterator(); entry.hasNext(); ) { Map.Entry eMap = entry.next(); System.out.println("移除真实节点为" + server + "的虚拟地址[" + eMap.getKey() + ":" + eMap.getValue() + "]"); virtualNodes.remove(eMap.getKey()); } //移除真实的物理节点--这里其实不需要移除也可以,如果将来不再接入时候可以考虑移除,但是2^32已经足够大了 } /** * 添加真实节点 * * @param server */ private static void addTrueNote(String server) { //添加真实节点,如果移除真实节点需要添加,没有移除的话就保留就好了 //添加虚拟节点 SortedMap rmVitureNode = generateVitureNode(server); for (Iterator> entry = rmVitureNode.entrySet().iterator(); entry.hasNext(); ) { Map.Entry eMap = entry.next(); System.out.println("生成真实节点为" + server + "的虚拟地址[" + eMap.getKey() + ":" + eMap.getValue() + "]"); } virtualNodes.putAll(rmVitureNode); } /** * 通过真实的物理机获得虚拟节点 * * @param trueNode * @return */ private static SortedMap generateVitureNode(String trueNode) { SortedMap virtualNodes = new TreeMap(); for (int i = 0; i < VIRTUAL_NODES; i++) { String virtualNodeName = trueNode + "&&VN" + String.valueOf(i); //这里可以删除 System.out.println("虚拟节点[" + virtualNodeName + "]被添加, hash值为" + getHash(virtualNodeName)); virtualNodes.put(getHash(virtualNodeName), virtualNodeName); } return virtualNodes; } //获得当前路由的节点 private static String getServer(String key) { //得到对应的hash值 int hash = getHash(key); //得到大于hash值中所有map SortedMap subMap = virtualNodes.tailMap(hash); String virtualNode; //注意下边实现了hash一致环 if (subMap.isEmpty()) { //如果当前的值没有大于该值的,从第一个节点开始 Integer i = virtualNodes.firstKey(); //返回对应的服务器地址 virtualNode = virtualNodes.get(i); } else { //顺时针下个节点位置,离当前hash值最近的那个位置 Integer i = subMap.firstKey(); //返回对应的服务器 virtualNode = subMap.get(i); }//virtualNode虚拟节点名称要截取一下 if (!StringUtils.isEmpty(virtualNode)) { return virtualNode.substring(0, virtualNode.indexOf("&&")); } return null; } public static void main(String[] args) { String[] keys = {"美国", "俄罗斯", "中国"}; for (String key : keys) System.out.println(key + "-的hash值为" + getHash(key) + ",被路由到节点" + getServer(key)); System.out.println(); try { Thread.sleep(1110); } catch (InterruptedException e) { e.printStackTrace(); } //移除某一真实的节点 delTrueNode("192.168.0.0:111"); System.out.println("移除物理机192.168.0.0:111后分配的hash地址 "); for (String key : keys) System.out.println(key + "-的hash值为" + getHash(key) + ",被路由到节点" + getServer(key)); System.out.println(); try { Thread.sleep(1101); } catch (InterruptedException e) { e.printStackTrace(); } addTrueNote("192.168.0.0:111"); System.out.println("添加物理机192.168.0.0:111后分配的hash地址 "); for (String key : keys) System.out.println(key + "-的hash值为" + getHash(key) + ",被路由到节点" + getServer(key)); } } 运行后的结果如下: 虚拟节点[192.168.0.0:111&&VN0]被添加, hash值为441276990 虚拟节点[192.168.0.0:111&&VN1]被添加, hash值为7212075 虚拟节点[192.168.0.0:111&&VN2]被添加, hash值为1956600191 虚拟节点[192.168.0.0:111&&VN3]被添加, hash值为880099582 虚拟节点[192.168.0.0:111&&VN4]被添加, hash值为1775594257 虚拟节点[192.168.0.1:111&&VN0]被添加, hash值为1589670672 虚拟节点[192.168.0.1:111&&VN1]被添加, hash值为1624001080 虚拟节点[192.168.0.1:111&&VN2]被添加, hash值为1773104951 虚拟节点[192.168.0.1:111&&VN3]被添加, hash值为1648864917 虚拟节点[192.168.0.1:111&&VN4]被添加, hash值为1605726925 虚拟节点[192.168.0.2:111&&VN0]被添加, hash值为281646770 虚拟节点[192.168.0.2:111&&VN1]被添加, hash值为866565852 虚拟节点[192.168.0.2:111&&VN2]被添加, hash值为1993194878 虚拟节点[192.168.0.2:111&&VN3]被添加, hash值为1815821653 虚拟节点[192.168.0.2:111&&VN4]被添加, hash值为1285435563 虚拟节点[192.168.0.3:111&&VN0]被添加, hash值为206429284 虚拟节点[192.168.0.3:111&&VN1]被添加, hash值为1198719942 虚拟节点[192.168.0.3:111&&VN2]被添加, hash值为1004742944 虚拟节点[192.168.0.3:111&&VN3]被添加, hash值为1620159605 虚拟节点[192.168.0.3:111&&VN4]被添加, hash值为1133756370 虚拟节点[192.168.0.4:111&&VN0]被添加, hash值为1109095769 虚拟节点[192.168.0.4:111&&VN1]被添加, hash值为458370133 虚拟节点[192.168.0.4:111&&VN2]被添加, hash值为1490699463 虚拟节点[192.168.0.4:111&&VN3]被添加, hash值为1198431876 虚拟节点[192.168.0.4:111&&VN4]被添加, hash值为554406 美国-的hash值为413938569,被路由到节点192.168.0.0:111 俄罗斯-的hash值为1196559832,被路由到节点192.168.0.4:111 中国-的hash值为1625483750,被路由到节点192.168.0.1:111 虚拟节点[192.168.0.0:111&&VN0]被添加, hash值为441276990 虚拟节点[192.168.0.0:111&&VN1]被添加, hash值为7212075 虚拟节点[192.168.0.0:111&&VN2]被添加, hash值为1956600191 虚拟节点[192.168.0.0:111&&VN3]被添加, hash值为880099582 虚拟节点[192.168.0.0:111&&VN4]被添加, hash值为1775594257 移除真实节点为192.168.0.0:111的虚拟地址[7212075:192.168.0.0:111&&VN1] 移除真实节点为192.168.0.0:111的虚拟地址[441276990:192.168.0.0:111&&VN0] 移除真实节点为192.168.0.0:111的虚拟地址[880099582:192.168.0.0:111&&VN3] 移除真实节点为192.168.0.0:111的虚拟地址[1775594257:192.168.0.0:111&&VN4] 移除真实节点为192.168.0.0:111的虚拟地址[1956600191:192.168.0.0:111&&VN2] 移除物理机192.168.0.0:111后分配的hash地址 美国-的hash值为413938569,被路由到节点192.168.0.4:111 俄罗斯-的hash值为1196559832,被路由到节点192.168.0.4:111 中国-的hash值为1625483750,被路由到节点192.168.0.1:111 虚拟节点[192.168.0.0:111&&VN0]被添加, hash值为441276990 虚拟节点[192.168.0.0:111&&VN1]被添加, hash值为7212075 虚拟节点[192.168.0.0:111&&VN2]被添加, hash值为1956600191 虚拟节点[192.168.0.0:111&&VN3]被添加, hash值为880099582 虚拟节点[192.168.0.0:111&&VN4]被添加, hash值为1775594257 生成真实节点为192.168.0.0:111的虚拟地址[7212075:192.168.0.0:111&&VN1] 生成真实节点为192.168.0.0:111的虚拟地址[441276990:192.168.0.0:111&&VN0] 生成真实节点为192.168.0.0:111的虚拟地址[880099582:192.168.0.0:111&&VN3] 生成真实节点为192.168.0.0:111的虚拟地址[1775594257:192.168.0.0:111&&VN4] 生成真实节点为192.168.0.0:111的虚拟地址[1956600191:192.168.0.0:111&&VN2] 添加物理机192.168.0.0:111后分配的hash地址 美国-的hash值为413938569,被路由到节点192.168.0.0:111 俄罗斯-的hash值为1196559832,被路由到节点192.168.0.4:111 中国-的hash值为1625483750,被路由到节点192.168.0.1:111