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

shz.core.hash.consistent.ConsistentHash Maven / Gradle / Ivy

There is a newer version: 2024.0.2
Show newest version
package shz.core.hash.consistent;

import shz.core.*;
import shz.core.hash.MdHash;

import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.function.LongPredicate;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 一致性哈希
 */
public abstract class ConsistentHash {
    /**
     * 虚拟节点数
     */
    private final int num;
    /**
     * 节点是否存储数据
     */
    private final boolean storage;
    /**
     * 节点集
     */
    private Set nodes;
    /**
     * 节点环
     */
    private volatile ConcurrentSkipListMap loop;

    protected ConsistentHash(int num, boolean storage) {
        if (num <= 0) throw new IllegalArgumentException("虚拟节点数不能小于1");
        this.num = num;
        this.storage = storage;
    }

    protected ConsistentHash(boolean storage) {
        this(128, storage);
    }

    /**
     * 获取节点
     */
    public final String get(String key) {
        if (NullHelp.isBlank(key)) return null;
        return NullHelp.isEmpty(loop) ? null : ceiling(hash(key));
    }

    protected long hash(String key) {
        byte[] bytes = MdHash.MD5.hash(key.getBytes(StandardCharsets.UTF_8));
        long v = (long) (bytes[3] & 0xff) << 24 | (bytes[2] & 0xff) << 16 | (bytes[1] & 0xff) << 8 | bytes[0] & 0xff;
        return v & 0xffffffffL;
    }

    public final void setNodes(Set nodes) {
        if (NullHelp.isEmpty(nodes)) return;
        nodes = nodes.stream().filter(NullHelp::nonBlank).collect(Collectors.toSet());
        if (NullHelp.isEmpty(nodes)) return;
        if (loop == null) {
            synchronized (this) {
                if (loop == null) {
                    this.nodes = nodes;
                    loop = new ConcurrentSkipListMap<>();
                    this.nodes.forEach(node -> {
                        for (int i = 0; i < num; ++i) loop.put(hash(joint(node, i)), node);
                    });
                    transferForDelete();
                    transferForAdd();
                }
            }
        }
    }

    @FunctionalInterface
    public interface Filter extends LongPredicate, Serializable {
    }

    ////////////////////////////////delete

    /**
     * 重新转移已经删除的节点数据(转移时若服务器宕机恢复后执行)
     */
    public final void transferForDelete() {
        Set nodes = getFailureNodesForDelete();
        if (NullHelp.nonEmpty(nodes)) nodes.stream().filter(NullHelp::nonBlank).forEach(node -> {
            Map map = getDeleteMap(node);
            if (NullHelp.nonEmpty(map)) {
                transferForDelete(node, map);
                cancelDeleteMap(node);
            }
            cancelDeleteMsg(node);
        });
    }

    /**
     * 获取转移失败的删除节点
     */
    protected Set getFailureNodesForDelete() {
        return null;
    }

    /**
     * 保存删除节点时的转移信息
     */
    protected void saveDeleteMap(String node, Map map) {
    }

    /**
     * 获取删除节点时的转移信息
     */
    protected Map getDeleteMap(String node) {
        return null;
    }

    /**
     * 取消删除节点时的转移信息
     */
    protected void cancelDeleteMap(String node) {
    }

    /**
     * 转移数据(当storage为true时有效)
     * 删除节点时,将删除节点数据转移到其余节点
     *
     * @param src  源节点(删除节点)
     * @param dest 目标节点及key过滤条件
     */
    protected abstract void transferForDelete(String src, Map dest);

    /**
     * 标记删除节点消息状态为尝试阶段
     */
    protected void attemptDeleteMsg(String node) {
    }

    /**
     * 取消删除节点消息
     */
    protected void cancelDeleteMsg(String node) {
    }

    /**
     * 删除节点
     */
    public final void delete(String node) {
        if (NullHelp.isBlank(node) || nodes == null || !nodes.contains(node)) return;
        attemptDeleteMsg(node);
        try {
            if (!nodes.remove(node)) return;
            if (nodes.isEmpty()) {
                loop.clear();
                clear(node);
                return;
            }

            long[] hashes = new long[num];
            for (int i = 0; i < num; ++i) {
                hashes[i] = hash(joint(node, i));
                loop.remove(hashes[i]);
            }
            if (!storage) return;

            Map longPredicateMap = ToMap.get(nodes.size()).build();
            for (int i = 0; i < num; ++i) {
                long hash = hashes[i];
                long floorKey = floorKey(hash);
                Filter predicate = v -> v > floorKey && v <= hash;
                String ceiling = ceiling(hash);
                Filter oldPredicate = longPredicateMap.get(ceiling);
                longPredicateMap.put(ceiling, oldPredicate == null ? predicate : v -> oldPredicate.test(v) || predicate.test(v));
            }

            saveDeleteMap(node, longPredicateMap);
            transferForDelete(node, longPredicateMap);
        } finally {
            cancelDeleteMsg(node);
        }
    }

    /**
     * 清除节点数据(当storage为true时有效)
     * 只有在所有节点不可用(删除)时才会触发此方法
     */
    protected abstract void clear(String node);

    public final void delete(Set nodes) {
        if (NullHelp.nonEmpty(nodes)) nodes.forEach(this::delete);
    }
    ////////////////////////////////delete

    ////////////////////////////////add

    /**
     * 重新转移数据到已经添加的节点(转移时若服务器宕机恢复后执行)
     */
    public final void transferForAdd() {
        Set nodes = getFailureNodesForAdd();
        if (NullHelp.nonEmpty(nodes)) nodes.stream().filter(NullHelp::nonBlank).forEach(node -> {
            Map map = getAddMap(node);
            if (NullHelp.nonEmpty(map)) {
                map.forEach((src, predicate) -> transferForAdd(src, key -> predicate.test(hash(key)), node));
                cancelAddMap(node);
            }
            cancelAddMsg(node);
        });
    }

    /**
     * 获取转移失败的新增节点
     */
    protected Set getFailureNodesForAdd() {
        return null;
    }

    /**
     * 保存新增节点时的转移信息
     */
    protected void saveAddMap(String node, Map map) {
    }

    /**
     * 获取新增节点时的转移信息
     */
    protected Map getAddMap(String node) {
        return null;
    }

    /**
     * 取消删除节点时的转移信息
     */
    protected void cancelAddMap(String node) {
    }

    /**
     * 转移数据(当storage为true时有效)
     * 添加节点时,从已有节点获取符合过滤条件keyFilter的数据转移到新增节点
     *
     * @param src       源节点
     * @param keyFilter 源节点key过滤条件
     * @param dest      目标节点(新增节点)
     */
    protected abstract void transferForAdd(String src, Predicate keyFilter, String dest);

    /**
     * 标记新增节点消息状态为尝试阶段
     */
    protected void attemptAddMsg(String node) {
    }

    /**
     * 取消新增节点消息
     */
    protected void cancelAddMsg(String node) {
    }

    /**
     * 添加节点
     */
    public final void add(String node) {
        if (NullHelp.isBlank(node) || nodes == null || nodes.contains(node)) return;
        attemptAddMsg(node);
        try {
            long[] hashes = new long[num];
            for (int i = 0; i < num; ++i) hashes[i] = hash(joint(node, i));

            Map map = null;
            if (storage && !nodes.isEmpty()) {
                map = ToMap.get(nodes.size()).build();
                for (int i = 0; i < num; ++i) {
                    long hash = hashes[i];
                    long floorKey = floorKey(hash);
                    Filter predicate = v -> v > floorKey && v <= hash;
                    String ceiling = ceiling(hash);
                    Filter oldPredicate = map.get(ceiling);
                    map.put(ceiling, oldPredicate == null ? predicate : v -> oldPredicate.test(v) || predicate.test(v));
                }

                saveAddMap(node, map);
            }

            nodes.add(node);
            for (int i = 0; i < num; ++i) loop.put(hashes[i], node);

            if (map != null)
                map.forEach((src, predicate) -> transferForAdd(src, key -> predicate.test(hash(key)), node));
        } finally {
            cancelAddMsg(node);
        }
    }

    public final void add(Set nodes) {
        if (NullHelp.nonEmpty(nodes)) nodes.forEach(this::add);
    }
    ////////////////////////////////add

    /**
     * 查找后置节点(或当前节点)
     */
    private String ceiling(long hash) {
        String node = loop.get(hash);
        if (node != null || loop.containsKey(hash)) return node;
        Long ceilingKey = loop.ceilingKey(hash);
        return loop.get(ceilingKey == null ? loop.firstKey() : ceilingKey);
    }

    /**
     * 查找前置节点key
     */
    private long floorKey(long hash) {
        Long floorKey = loop.floorKey(hash);
        return floorKey == null ? loop.lastKey() : floorKey;
    }

    private String joint(String node, int i) {
        return node + "-" + i;
    }

    public final int getNum() {
        return num;
    }

    public final boolean isStorage() {
        return storage;
    }

    public final Set getNodes() {
        return nodes;
    }

    public final ConcurrentSkipListMap getLoop() {
        return loop;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy