import com.zliot.zltom.common.Constants;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
/**
*
* 一致性HASH算法
*
* @author zhuhy
* @version zltom1.0.0,2021-7-5
* @since zltom1.0.0
*/
public class ConsistentHash
{
//一台物理服务器虚拟为多少个虚拟服务器节点
private final static int VIRTUAL_NODE_NUM = 150;
//一致性HASH环
private final SortedMap<Integer, String> hashCircleNodes =
new TreeMap<Integer, String>();
/**
* 构造方法
* 根据节点的HASH值,将服务器节点放置到HASH环上
*
* @param nodeNames 真实服务节点名称列表
*/
public ConsistentHash(List<String> nodeNames)
{
for (String nodeName : nodeNames)
{
addNode(nodeName);
}
}
/**
* 增加一台物理服务器节点
* @param nodeName
*/
public void addNode(String nodeName)
{
//往环中增加对应的虚拟节点
for (int i = Constants.NUM_ZERO; i < VIRTUAL_NODE_NUM; i++)
{
hashCircleNodes.put(getHashCode(nodeName + i), nodeName);
}
}
/**
* 删除一台物理服务器节点
* @param nodeName
*/
public void removeNode(String nodeName)
{
//在环中删除对应的虚拟节点
for (int i = Constants.NUM_ZERO; i < VIRTUAL_NODE_NUM; i++)
{
hashCircleNodes.remove(getHashCode(nodeName + i));
}
}
/**
* 根据KEY获取路由节点名称
* @param key 关键字
* @return
*/
public String getNode(String key)
{
if(hashCircleNodes.isEmpty())
{
return null;
}
int hashCode = getHashCode(key);
System.out.println("hashcode:" + hashCode);
//如果没有命中节点,则从环中获取最近的节点KEY
if (!hashCircleNodes.containsKey(hashCode))
{
hashCode = getNearestKey(hashCode);
}
return hashCircleNodes.get(hashCode);
}
/**
* 从HASH环中获取最接近的KEY
* @param key
* @return
*/
private Integer getNearestKey(Integer key)
{
Integer nearestKey;
if (hashCircleNodes.containsKey(key))
{
nearestKey = key;
}
else
{
SortedMap<Integer, String> tailMap = hashCircleNodes.tailMap(key);
SortedMap<Integer, String> headMap = hashCircleNodes.headMap(key);
if(null == tailMap)
{
nearestKey = hashCircleNodes.firstKey();
}
else if(null == headMap)
{
nearestKey = hashCircleNodes.lastKey();
}
else
{
if((headMap.lastKey() - key) < (key -tailMap.firstKey()))
{
nearestKey = headMap.lastKey();
}
else
{
nearestKey = tailMap.firstKey();
}
}
}
return nearestKey;
}
/**
* 获取输入数据的HASH值
* @param data
* @return
*/
public int getHashCode(String data)
{
//使用FNV HASH算法,使得接近的数据内容可以得到相差较大的HASH值
//return data.hashCode();
return fnvHash(data);
}
/**
* 改进的32位FNV算法1
* https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function
* @param data 字符串
* @return hash结果
*/
public int fnvHash(String data) {
final int p = 16777619;
int hash = (int) 2166136261L;
for (int i = 0; i < data.length(); i++) {
hash = (hash ^ data.charAt(i)) * p;
}
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
return Math.abs(hash);
}
/**
* 主方法测试
* @param args
*/
public static void main(String[] args)
{
List<String> nodeNames = new ArrayList<String>();
nodeNames.add("服务器组1");
nodeNames.add("服务器组2");
nodeNames.add("服务器组3");
nodeNames.add("服务器组4");
ConsistentHash consistentHash = new ConsistentHash(nodeNames);
System.out.println("服务器组1:" + consistentHash.fnvHash("服务器组1"));
System.out.println("服务器组2:" + consistentHash.fnvHash("服务器组2"));
System.out.println("服务器组3:" + consistentHash.fnvHash("服务器组3"));
System.out.println("服务器组4:" + consistentHash.fnvHash("服务器组4"));
String key = "dc48a8b8-4569-4743-9b40-3475f960bf29";
System.out.println(consistentHash.getNode(key));
String key1 = "dc48a8b8-c301-4743-9b40-3475f960bf29";
System.out.println(consistentHash.getNode(key1));
String key2 = "dc48a8b8-c301-4743-9865-3475f960be26";
System.out.println(consistentHash.getNode(key2));
String key3 = "cde9ceee-483e-4b23-a2b1-e63bf6976d20";
System.out.println(consistentHash.getNode(key3));
String key4 = "e048a8b8-c301-4743-9b40-3475f960bf29";
System.out.println(consistentHash.getNode(key4));
String key5 = "5558a8b8-c301-4743-9b40-3475f960bf29";
System.out.println(consistentHash.getNode(key5));
System.out.println("============================");
consistentHash.removeNode("服务器组4");
System.out.println(consistentHash.getNode(key));
System.out.println(consistentHash.getNode(key1));
System.out.println(consistentHash.getNode(key2));
System.out.println(consistentHash.getNode(key3));
System.out.println(consistentHash.getNode(key4));
System.out.println(consistentHash.getNode(key5));
System.out.println("============================");
consistentHash.addNode("服务器组4");
System.out.println(consistentHash.getNode(key));
System.out.println(consistentHash.getNode(key1));
System.out.println(consistentHash.getNode(key2));
System.out.println(consistentHash.getNode(key3));
System.out.println(consistentHash.getNode(key4));
System.out.println(consistentHash.getNode(key5));
System.out.println("============================");
consistentHash.addNode("服务器组5");
System.out.println(consistentHash.getNode(key));
System.out.println(consistentHash.getNode(key1));
System.out.println(consistentHash.getNode(key2));
System.out.println(consistentHash.getNode(key3));
System.out.println(consistentHash.getNode(key4));
System.out.println(consistentHash.getNode(key5));
System.out.println("============================");
}
}