算法原理分析
请参考:https://www.cnblogs.com/lpfuture/p/5796398.html
源码示例
import org.apache.commons.lang3.StringUtils;
import java.util.LinkedList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
public class ConsistentHashing {
//待添加入Hash环的服务器列表
private 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"};
//真实节点列表
private static List<String> realNodes = new LinkedList<>();
//虚拟节点列表
private static SortedMap<Integer, String> sortedMap = new TreeMap<>();
// 真实节点对应的虚拟节点数
private static final int NUM_HOST = 5;
//程序初始化,将所有的服务器放入sortedMap中
static {
//添加真实节点
for (int i = 0; i < servers.length; i++) {
realNodes.add(servers[i]);
}
//添加虚拟节点
for (String str : realNodes) {
for (int i = 1; i <= NUM_HOST; i++) {
String nodeName = str + "VM" + String.valueOf(i);
int hash = getHash(nodeName);
sortedMap.put(hash, nodeName);
System.out.println("虚拟节点hash:" + hash + "【" + nodeName + "】放入");
}
}
}
//得到应当路由到的结点
private static String getServer(String key) {
//得到该key的hash值
int hash = getHash(key);
//得到大于该Hash值的所有Map
String host;
SortedMap<Integer, String> subMap = sortedMap.tailMap(hash);
if (subMap.isEmpty()) {
//如果没有比该key的hash值大的,则从第一个node开始
Integer i = sortedMap.firstKey();
//返回对应的服务器
host = sortedMap.get(i);
} else {
//第一个Key就是顺时针过去离node最近的那个结点
Integer i = subMap.firstKey();
//返回对应的服务器
host = subMap.get(i);
}
if (StringUtils.isNotBlank(host)) {
String realHost = host.substring(0, host.indexOf("VM"));
return realHost;
}
return null;
}
//使用FNV1_32_HASH算法计算服务器的Hash值
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;
}
// 测试
public static void main(String[] args) {
String[] keys = {"1", "2","5", "6", "14","15"};
for (int i = 0; i < keys.length; i++) {
System.out.println("[" + keys[i] + "]的hash值为" +
getHash(keys[i]) + ", 被路由到结点[" + getServer(keys[i]) + "]");
}
}
}
测试输出结果
虚拟节点hash:203600595【192.168.0.0:111VM1】放入
虚拟节点hash:533864743【192.168.0.0:111VM2】放入
虚拟节点hash:283014282【192.168.0.0:111VM3】放入
虚拟节点hash:112805468【192.168.0.0:111VM4】放入
虚拟节点hash:2047670539【192.168.0.0:111VM5】放入
虚拟节点hash:519865065【192.168.0.1:111VM1】放入
虚拟节点hash:1154856732【192.168.0.1:111VM2】放入
虚拟节点hash:1253620420【192.168.0.1:111VM3】放入
虚拟节点hash:271923136【192.168.0.1:111VM4】放入
虚拟节点hash:954403340【192.168.0.1:111VM5】放入
虚拟节点hash:1573046012【192.168.0.2:111VM1】放入
虚拟节点hash:1216691265【192.168.0.2:111VM2】放入
虚拟节点hash:1427550122【192.168.0.2:111VM3】放入
虚拟节点hash:563194059【192.168.0.2:111VM4】放入
虚拟节点hash:1191243314【192.168.0.2:111VM5】放入
虚拟节点hash:449809950【192.168.0.3:111VM1】放入
虚拟节点hash:1517398358【192.168.0.3:111VM2】放入
虚拟节点hash:1061446677【192.168.0.3:111VM3】放入
虚拟节点hash:1711703068【192.168.0.3:111VM4】放入
虚拟节点hash:888576886【192.168.0.3:111VM5】放入
虚拟节点hash:300861392【192.168.0.4:111VM1】放入
虚拟节点hash:185778140【192.168.0.4:111VM2】放入
虚拟节点hash:70846991【192.168.0.4:111VM3】放入
虚拟节点hash:20072546【192.168.0.4:111VM4】放入
虚拟节点hash:294895133【192.168.0.4:111VM5】放入
虚拟节点hash:1533857590【192.168.0.5:111VM1】放入
虚拟节点hash:1043993658【192.168.0.5:111VM2】放入
虚拟节点hash:1031128652【192.168.0.5:111VM3】放入
虚拟节点hash:1197352122【192.168.0.5:111VM4】放入
虚拟节点hash:662112606【192.168.0.5:111VM5】放入
[1]的hash值为1081142246, 被路由到结点[192.168.0.1:111]
[2]的hash值为1310673766, 被路由到结点[192.168.0.2:111]
[5]的hash值为1039214538, 被路由到结点[192.168.0.5:111]
[6]的hash值为853429834, 被路由到结点[192.168.0.3:111]
[14]的hash值为2038748830, 被路由到结点[192.168.0.0:111]
[15]的hash值为2102931218, 被路由到结点[192.168.0.4:111]