目的:通过一致性哈希将数据库待处理的数据,通过主键,分配到对应的节点处理
public class ConsistentHashCode {
/**
* 真实节点数
*/
private Integer realNodesTotal;
/**
* 虚拟节点数量,一个真实节点对应500个虚拟节点
*/
private static final Integer virtual_node_count = 500;
/**
* 实际节点列表如: {0,1,2,3,4}
*/
private List<Integer> realNodes;
/**
* key 表示服务器的hash值,value表示服务器的虚拟节点名称
*/
private SortedMap<Integer, String> virtualNodes;
public ConsistentHashCode(Integer realNodesTotal) {
if (realNodesTotal <= 0) {
throw new RuntimeException("ConsistentHashCode 初始化失败:节点数量不能小于等于 0");
}
this.realNodesTotal = realNodesTotal;
this.realNodes = new LinkedList<>();
this.virtualNodes = new TreeMap<>();
for (int i = 0; i < realNodesTotal; i++) {
realNodes.add(i);
}
realNodes.stream().forEach(realNodes -> {
for (int i = 0; i < virtual_node_count; i++) {
String virtualNodeName = realNodes + "&&VN" + String.valueOf(i);
Integer hash = getHash(virtualNodeName);
virtualNodes.put(hash, virtualNodeName);
}
});
}
/**
* 获取value应该被分配到的节点序号
*
* @param value
* @return
*/
public Integer getServer(String value) {
//获取 value的哈希值
Integer hash = getHash(value);
//得到大于等于该Hash值的所有Map
SortedMap<Integer, String> subMap = virtualNodes.tailMap(hash);
if (!subMap.isEmpty()) {
//获取第一个key,也就是顺时针过去离node最接近的那个节点
Integer firstKey = subMap.firstKey();
String virtualNodeName = subMap.get(firstKey);
return Integer.parseInt(virtualNodeName.substring(0, virtualNodeName.indexOf("&&VN")));
} else {
return 0;
}
}
/**
* 使用FNV1_32_HASH算法计算Hash值
* @param key
* @return
*/
public Integer getHash(String key) {
final int p = 16777619;
int hash = (int) 2166136261L;
for (int i = 0; i < key.length(); i++)
hash = (hash ^ key.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;
}
}