shz.core.hash.consistent.ConsistentHash Maven / Gradle / Ivy
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