支持虛擬節點+加權,由於不一樣的節點可能資源配置不一樣,加權能夠使負載均衡最大化,虛擬節點,能夠下降某個節點出現問題後對整個哈希環的衝擊java
考慮到不一樣場景用來做哈希的key可能不同,因此提供一個包裝類Node,能夠自定義key,且能夠自定義權重node
安全問題,添加節點和刪除節點是須要重建哈希環,此處要考慮併發狀況的發生(此處暫未實現),通常狀況下只需初始化一次便可。安全
哈希後的取值,本處爲了通用採用String類型,能夠針對特殊場景做優化,我的認爲只有最適合的,沒有最好的併發
public final class Node<T> { public static final int DEFAULT_WEIGHT = 1; private T target; private String key; private int weight = DEFAULT_WEIGHT; public Node(T target) { this.target = target; this.key = target.toString(); } public Node(T target, String key) { this.target = target; this.key = key; } public Node(T target, String key, int weight) { this.target = target; this.key = key; this.weight = weight; } public T getTarget() { return target; } public void setTarget(T target) { this.target = target; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } }
import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; import java.nio.charset.Charset; import java.util.*; /** * 一致性哈希 * @param <T> */ public class ConsistentHash<T> { //哈希函數 private HashFunction hashFunction = Hashing.murmur3_32(); //哈希環 private TreeMap<String, Node<T>> nodeMappings = new TreeMap<>(); //虛擬節點數 private int replicas = 160; //真實節點 private List<Node<T>> realNodes; public ConsistentHash(HashFunction hashFunction, List<Node<T>> realNodes, int replicas) { this.hashFunction = hashFunction; this.realNodes = realNodes; this.replicas = replicas; } public ConsistentHash(HashFunction hashFunction, List<Node<T>> realNodes) { this.hashFunction = hashFunction; this.realNodes = realNodes; } public ConsistentHash(List<Node<T>> realNodes, int replicas) { this.realNodes = realNodes; this.replicas = replicas; } public ConsistentHash(List<Node<T>> realNodes) { this.realNodes = realNodes; } /** * 初始化哈希環 */ public void init(){ realNodes.forEach(node -> { for (int i = 0; i < replicas*node.getWeight(); i++) { StringBuilder sb = new StringBuilder(node.getKey()); sb.append(i); nodeMappings.put(hashCode(sb.toString()), node); } }); } public String hashCode(String key){ return hashFunction.hashString(key, Charset.defaultCharset()).asBytes().toString(); } public Node<T> getNode(String key){ SortedMap<String, Node<T>> tailMap = nodeMappings.tailMap(hashCode(key)); if(tailMap.isEmpty()){ return nodeMappings.get(nodeMappings.firstKey()); } return tailMap.get(tailMap.firstKey()); } public static void main(String[] args) { List<Node<String>> realNodes = Arrays.asList(new Node("127.0.0.1"),new Node("192.168.4.175"),new Node("192.168.3.175"), new Node("172.147.0.101")); ConsistentHash consistentHash = new ConsistentHash(Hashing.md5(), realNodes); consistentHash.init(); List<Node> one = new ArrayList<>(); List<Node> two = new ArrayList<>(); List<Node> three = new ArrayList<>(); List<Node> four = new ArrayList<>(); for (int i = 0; i <10000 ; i++) { Node<String> node = consistentHash.getNode("node"+i); if(node.getKey().equals("127.0.0.1")){ one.add(node); } if(node.getKey().equals("192.168.4.175")){ two.add(node); } if(node.getKey().equals("192.168.3.175")){ three.add(node); } if(node.getKey().equals("172.147.0.101")){ four.add(node); } } System.out.println(one.size()); System.out.println(two.size()); System.out.println(three.size()); System.out.println(four.size()); }