ref[move]: move hash to net package

This commit is contained in:
godotg
2023-09-02 19:17:15 +08:00
parent 00bb1e37a0
commit e158e92497
6 changed files with 4 additions and 105 deletions
@@ -22,7 +22,7 @@ import com.zfoo.protocol.collection.CollectionUtils;
import com.zfoo.protocol.exception.RunException;
import com.zfoo.protocol.model.Pair;
import com.zfoo.protocol.registration.ProtocolModule;
import com.zfoo.util.math.ConsistentHash;
import com.zfoo.net.util.ConsistentHash;
import org.springframework.lang.Nullable;
import java.util.TreeMap;
@@ -0,0 +1,77 @@
/*
* Copyright (C) 2020 The zfoo Authors
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the License.
*/
package com.zfoo.net.util;
import com.zfoo.protocol.model.Pair;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.TreeMap;
/**
* 带虚拟节点的一致性Hash算法,参考:http://www.zsythink.net/archives/1182
*
* @author godotg
* @version 3.0
*/
public class ConsistentHash<K, V> {
// 真实结点列表,考虑到服务器上线、下线的场景,即添加、删除的场景会比较频繁,这里使用LinkedList会更好
private List<Pair<K, V>> realNodes = new ArrayList<>();
// 虚拟节点,key表示虚拟节点的hash值,value表示虚拟节点的名称
private TreeMap<Integer, Pair<K, V>> virtualNodeTreeMap = new TreeMap<>();
// 虚拟节点的数目,数量越大约均匀
private int virtualNodes = 0;
public ConsistentHash(List<Pair<K, V>> realNodes, int virtualNodes) {
// 先把原始的服务器添加到真实结点列表中
this.realNodes.addAll(realNodes);
this.virtualNodes = virtualNodes;
// 初始化
// 再添加虚拟节点,遍历LinkedList使用foreach循环效率会比较高
for (var realNode : realNodes) {
addNode(realNode);
}
}
public void addNode(Pair<K, V> realNode) {
for (var i = 0; i < this.virtualNodes; i++) {
var virtualNode = realNode.getKey().toString() + "&&VN" + i;
var hash = HashUtils.fnvHash(virtualNode);
virtualNodeTreeMap.put(hash, realNode);
}
}
// 得到应当路由到的结点
public Pair<K, V> getRealNode(Object key) {
// 得到该key的hash值
var hash = HashUtils.fnvHash(key);
// 第一个Key就是顺时针过去离node最近的那个结点
var entry = virtualNodeTreeMap.ceilingEntry(hash);
if (Objects.isNull(entry)) {
// 如果没有比该key的hash值大的,则从第一个node开始
return virtualNodeTreeMap.firstEntry().getValue();
}
return entry.getValue();
}
public TreeMap<Integer, Pair<K, V>> getVirtualNodeTreeMap() {
return virtualNodeTreeMap;
}
}
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2020 The zfoo Authors
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the License.
*/
package com.zfoo.net.util;
/**
* 改进的hash算法虽然发布比较均匀,但是没有Java自带的hash算法速度快,所以需要知道自己需要什么
*
* @author godotg
* @version 3.0
*/
public abstract class HashUtils {
private static final int P = 16777619;
private static final int INIT_HASH = (int) 2166136261L;
/**
* 改进的32位FNV算法1
*
* @param data 数组
* @return hash结果
*/
public static int fnvHash(byte[] data) {
var hash = INIT_HASH;
for (byte b : data) {
hash = (hash ^ b) * P;
}
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
return Math.abs(hash);
}
/**
* 改进的32位FNV算法1
*
* @param object 计算hash的对象,会调用toString方法
* @return hash结果
*/
public static int fnvHash(Object object) {
var hash = object.toString().chars().reduce(INIT_HASH, (left, right) -> (left ^ right) * P);
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
return Math.abs(hash);
}
}