分布式服务器布置需要使用到hash算法,设节点有N个,普通的哈希算法:key%N,在遇到节点增加和减少的情况下,对于单纯的逻辑计算hash是没有问题的,但如果节点集群提供存储功能,那效果就不理想了。这时候我们的想法是在增加节点时以前的节点不受影响,在减少节点时只有与减少节点相关的受影响。那么我们不能单纯的使用%的方式,有个解决方案是将对象key和服务器节点key抽象出来,间隔排布在一个圆环上面,安装顺时针的方式对象key取的最近的服务器节点。这个方法就是ConsistentHash,下面贴出算法的Java版本:
import java.util.Collection;
import java.util.SortedMap;
import java.util.TreeMap;
public class ConsistentHash<T> {
private final HashFunction hashFunction;// hash算法
private final int numberOfReplicas;// 虚拟节点数目
private final SortedMap<Integer, T> circle = new TreeMap<Integer, T>();
public ConsistentHash(HashFunction hashFunction, int numberOfReplicas,
Collection<T> nodes){ // 物理节点
this.hashFunction = hashFunction;
this.numberOfReplicas = numberOfReplicas;
for (T node : nodes) {
add(node);
}
}
public void add(T node) {
for (int i = 0; i < numberOfReplicas; i++) {
circle.put(hashFunction.hash(node.toString() + i), node);
}
}
public void remove(T node) {
for (int i = 0; i < numberOfReplicas; i++) {
circle.remove(hashFunction.hash(node.toString() + i));
}
}
public T get(Object key) {// 关键算法
if (circle.isEmpty()) {
return null;
}
// 计算hash值
int hash = hashFunction.hash(key);
// 如果不包括这个hash值
if (!circle.containsKey(hash)) {
SortedMap<Integer, T> tailMap = circle.tailMap(hash);
hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
}
return circle.get(hash);
}
}
一种实现方案:
使用tree管理服务器节点,每个服务器节点保存一份能索引到它的hash key列表(实际应用以分布式IP哈希为居多),访问的时候计算key的hash值与tree管理的列表进行对比查找,找到对应的映射服务器。tree管理的服务器节点不太好决定的话只能是根据keyhash值和服务器数量进行虚拟节点扩展。