All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kaizen4j.common.algorithm.hash.ConsistentHashingShard Maven / Gradle / Ivy

There is a newer version: 1.3.8.RELEASE
Show newest version
package org.kaizen4j.common.algorithm.hash;

import com.google.common.base.Preconditions;
import org.apache.commons.collections4.CollectionUtils;
import org.kaizen4j.common.base.Symbols;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 基于 MurmurHash 32 位的一致性 hash 算法。 默认虚拟节点数量为:100。
 */
public class ConsistentHashingShard {

    private static Logger logger = LoggerFactory.getLogger(ConsistentHashingShard.class);

    private static final int DEFAULT_SEED = 0x1234ABCD;

    private static final int DEFAULT_VIRTUAL_NODE_NUM = 100;

    private static final String VIRTUAL_NODE_PREFIX = "VN";

    private int seed;

    private int virtualNodeNum;

    private SortedMap> virtualNodeMap;

    public ConsistentHashingShard(List> realNodes) {
        this(realNodes, DEFAULT_SEED, DEFAULT_VIRTUAL_NODE_NUM);
    }

    public ConsistentHashingShard(List> realNodes, int seed) {
        this(realNodes, seed, DEFAULT_VIRTUAL_NODE_NUM);
    }

    public ConsistentHashingShard(List> realNodes, int seed, int virtualNodeNum) {
        this.seed = seed;
        this.virtualNodeNum = virtualNodeNum;
        this.virtualNodeMap = new TreeMap<>();
        initVirtualNode(realNodes);
    }

    private void initVirtualNode(List> realNodes) {
        Preconditions.checkArgument(CollectionUtils.isNotEmpty(realNodes));

        for (WeightedShard weightedShard : realNodes) {
            // 加入权重,来控制虚拟节点的数量(virtualNodeNum * weight),从而控制节点被获取到的概率。
            int nodeNum = virtualNodeNum * weightedShard.getWeight();

            for (int n = 0; n < nodeNum; n++) {
                String virtualName = getVirtualName(weightedShard.getServer(), n);
                int hash = hash(virtualName);
                virtualNodeMap.put(hash, weightedShard);
            }
        }
    }

    /**
     * 生成虚拟节点的名称
     * 
     * @param t 实现 toString 方法的服务节点对象
     * @param n 虚拟节点的索引
     * @return 虚拟节点的名称
     */
    public String getVirtualName(T t, int n) {
        return t.toString() + Symbols.COLON + VIRTUAL_NODE_PREFIX + n;
    }

    public T getShardInfo(String key) {
        SortedMap> tailMap = virtualNodeMap.tailMap(hash(key));
        if (0 == tailMap.size()) {
            return virtualNodeMap.get(virtualNodeMap.firstKey()).getServer();
        }
        return tailMap.get(tailMap.firstKey()).getServer();
    }

    public int hash(String key) {
        return hash(ByteBuffer.wrap(key.getBytes()), seed);
    }

    /**
     * Hashes the bytes in a buffer from the current position to the limit.
     * 
     * @param buf The bytes to hash.
     * @param seed The seed for the hash.
     * @return The 32 bit murmur hash of the bytes in the buffer.
     */
    private int hash(ByteBuffer buf, int seed) {
        // save byte order for later restoration
        ByteOrder byteOrder = buf.order();
        buf.order(ByteOrder.LITTLE_ENDIAN);

        int m = 0x5bd1e995;
        int r = 24;

        int h = seed ^ buf.remaining();

        while (buf.remaining() >= 4) {
            int k = buf.getInt();

            k *= m;
            k ^= k >>> r;
            k *= m;

            h *= m;
            h ^= k;
        }

        if (buf.remaining() > 0) {
            ByteBuffer finish = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
            // for big-endian version, use this first:
            if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
                finish.position(4 - buf.remaining());
            }
            finish.put(buf).rewind();
            h ^= finish.getInt();
            h *= m;
        }

        h ^= h >>> 13;
        h *= m;
        h ^= h >>> 15;

        buf.order(byteOrder);
        if (h < 0) {
            h = Math.abs(h);
        }
        return h;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy