分布式服务器布置需要使用到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值和服务器数量进行虚拟节点扩展。


03-14 20:54