一致性哈希java實現

值得注意的點

  1. 哈希函數的選擇
  • murmur哈希函數
    該函數是非加密型哈希,性能高,且發生哈希碰撞的機率聽說很低
  • md5
  • SHA
  • 能夠選擇guava包,提供了豐富的哈希函數的API
  1. 支持虛擬節點+加權,由於不一樣的節點可能資源配置不一樣,加權能夠使負載均衡最大化,虛擬節點,能夠下降某個節點出現問題後對整個哈希環的衝擊java

  2. 考慮到不一樣場景用來做哈希的key可能不同,因此提供一個包裝類Node,能夠自定義key,且能夠自定義權重node

  3. 安全問題,添加節點和刪除節點是須要重建哈希環,此處要考慮併發狀況的發生(此處暫未實現),通常狀況下只需初始化一次便可。安全

  4. 哈希後的取值,本處爲了通用採用String類型,能夠針對特殊場景做優化,我的認爲只有最適合的,沒有最好的併發

實現

  1. 包裝類node
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;
    }
}
  1. 一致性哈希
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());
    }
相關文章
相關標籤/搜索