做个记录 防止老了忘掉
1)实现代码:
思路:
1)使用md5 加密节点名 得到byte[]
2)将byte[] 转换为整数 得到节点位置
3)使用TreeMap 记录 所有的 节点名 和节点对应的位置(一个节点对应多个虚拟位置)节点位置为key 节点名为value
格式为:
// 节点键值集合
private TreeMap<Integer, String> nodes = new TreeMap<Integer, String>();
4)使用 HashMap 记录 单个节点 和 其对应的所有虚假节点 (方便移除节点等其他操作)
格式为:
// 记录每个node 对应的 虚拟节点集合
private Map<String, List<Integer>> nodeposition = new HashMap<String, List<Integer>>();
2)完整代码:
package yuan;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* 一致性hash 算法
*
* @author Yuan
*
*/
public class ConsistentHash {
// 节点键值集合
private TreeMap<Integer, String> nodes = new TreeMap<Integer, String>();
// 记录每个node 对应的 虚拟节点集合
private Map<String, List<Integer>> nodeposition = new HashMap<String, List<Integer>>();
// 一个节点的虚拟节点数
private int nodeMul = 64;
// public
public void addNode(String nodeName) {
// 无虚拟节点写法
// Integer position = MD5Utils.GetMD5CodeToInt(nodeName);
// nodes.put(position, nodeName);
// positions = nodes.keySet();
// 引入虚拟节点
List<Integer> nodeposi = new ArrayList<Integer>();
for (int i = 0; i < nodeMul; i++) {
Integer posi = MD5Utils.GetMD5CodeToInt(nodeName + "-" + i);
nodeposi.add(posi);
nodes.put(posi, nodeName);
}
nodeposition.put(nodeName, nodeposi);
}
public void removeNode(String nodeName) {
List<Integer> nodepos = nodeposition.get(nodeName);
for (Integer posi : nodepos) {
nodes.remove(posi);
}
}
public String lookup(String key) {
Integer keyPos = MD5Utils.GetMD5CodeToInt(key);
Set<Integer> positions =nodes.keySet();
System.out.println("keyMd5Pos " + keyPos);
// 默认先获取第一个节点
String nodeName = nodes.get(nodes.firstKey());
for (Integer posi : positions) {
if (keyPos <= posi) {
nodeName = nodes.get(posi);
break;
}
}
return nodeName;
}
public void printNodes() {
Set<Integer> positions = nodes.keySet();
for (Integer posi : positions) {
System.out.println(nodes.get(posi) + " " + posi);
}
}
}
package yuan;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/*
* MD5 算法
*/
public class MD5Utils {
public MD5Utils() {
}
// 返回形式只为数字
private static int byteToInt(byte[] bByte) {
int value = 0;
// 由高位到低位
for (int i = 0; i < 4; i++) {
int shift = (4 - 1 - i) * 8;
value += (bByte[i] & 0x000000FF) << shift;// 往高位游
}
return value;
}
public static int GetMD5CodeToInt(String strObj) {
int result = 0;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
// md.digest() 该函数返回值为存放哈希值结果的byte数组
result = byteToInt(md.digest(strObj.getBytes()));
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
return result;
}
}
3)简单调用:
package yuan;
public class Test {
public static void main(String[] args) {
ConsistentHash hash = new ConsistentHash();
// 添加节点
hash.addNode("a");
hash.addNode("b");
hash.addNode("c");
// 打印所有的节点信息
hash.printNodes();
// 获取某个 key 的落点 即命中 那个节点
String nodeName = hash.lookup("name");
System.out.println(nodeName);
System.out.println("==================================");
// 移除节点
hash.removeNode("a");
hash.printNodes();
}
}