io.github.icodegarden.commons.lang.algorithm.consistenthash.ConsistentHashRouter Maven / Gradle / Ivy
package io.github.icodegarden.commons.lang.algorithm.consistenthash;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
import io.github.icodegarden.commons.lang.algorithm.HashFunction;
import io.github.icodegarden.commons.lang.algorithm.MD5Function;
/**
* 将节点对象散列到具有一定数量虚拟节点的散列环中
* 一致性hash的目的是当物理节点删除时,只需调整落到该节点的数据到新的物理节点,避免大规模的调整引起系统的不稳定
*
* @author Fangfang.Xu
*/
public class ConsistentHashRouter {
private final SortedMap> ring = new TreeMap>();
private final HashFunction hashFunc;
public ConsistentHashRouter(Collection pNodes, int vNodeCount) {
this(pNodes, vNodeCount, new MD5Function());
}
public ConsistentHashRouter(Collection pNodes, int vNodeCount, HashFunction hashFunc) {
this.hashFunc = hashFunc;
if (pNodes != null) {
for (T pNode : pNodes) {
addOrUpdatePhysicalNode(pNode, vNodeCount);
}
}
}
/**
* 在hash环上新增物理节点或在原物理节点上追加vNodeCount数量的虚拟节点
*
* 新增:每个虚拟机节点都新建,number递增
* 追加:先查出已存在的虚拟节点数,在这个基础上追加vNodeCount数量的虚拟节点
*/
public void addOrUpdatePhysicalNode(T pNode, int vNodeCount) {
if (vNodeCount < 0)
throw new IllegalArgumentException("illegal virtual node counts :" + vNodeCount);
int existingCount = countExistingVirtualNodes(pNode);
for (int i = 0; i < vNodeCount; i++) {
VirtualNode vNode = new VirtualNode(pNode, i + existingCount);
ring.put(hashFunc.hash(vNode.getKey()), vNode);
}
}
/**
* 移除物理节点,把该物理节点对应的虚拟节点从环上remove
*
* @return 移除的虚拟节点
*/
public void removePhysicalNode(T pNode) {
Iterator>> it = ring.entrySet().iterator();
while (it.hasNext()) {
Entry> next = it.next();
VirtualNode virtualNode = next.getValue();
if (virtualNode.isVirtualNodeOf(pNode)) {
it.remove();
}
}
}
/**
* 根据key找到该key应该落到的虚拟节点
*/
public VirtualNode routeNode(String key) {
if (ring.isEmpty()) {
return null;
}
/**
* 用跟虚拟节点一样的hash算法
*/
Number hashVal = hashFunc.hash(key);
/**
* tail后,得到的是 >= 传参的hash值的 那段hash环,有可能是空的
*/
SortedMap> tailMap = ring.tailMap(hashVal);
/**
* 不为空,应该落到最近的虚拟节点对应的物理节点; 为空,即相当于要落到整个hash环的第一个虚拟节点对应的物理节点
*/
Number nodeHashVal = !tailMap.isEmpty() ? tailMap.firstKey() : ring.firstKey();
return ring.get(nodeHashVal);
}
/**
* 获取物理节点已存在的虚拟节点数量
*/
public int countExistingVirtualNodes(T pNode) {
int count = 0;
for (VirtualNode vNode : ring.values()) {
if (vNode.isVirtualNodeOf(pNode)) {
count++;
}
}
return count;
}
/**
* 获取所有的虚拟节点数量
*/
public int countExistingVirtualNodes() {
return ring.size();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy