Week 004 作业

用户头像
徐培
关注
发布于: 2020 年 07 月 08 日

第五周的作业是写一个一致性哈希的算法并验证,只写了大概思路的架子。还差一段距离。

正在加紧调试中......

##直接贴代码了....其实并不能用,只通过注释大概写了下思路.....今晚到明天尽量调试成功再更新:

目前更新好了,代码简单测试可用,但性能似乎有低下,且没有更专业的测试,太晚了,明天排查下为什么那么慢。

PS: 我思考了下,有个不成熟的思考,不过不清楚对不对:其实对于哈希函数来说,我们的目的就是为了在尽可能少的空间占用情况下,换取更平均的Hash冲突率。

其实在Hash环上用了虚拟IP,仅仅是增加了Hash算法的容错性。让一个不那么好的Hash算法,也有概率、或者说尽量达到比较完美的Hash算法结果。 如果是一个绝对均匀的IP算法,从概率上来讲不需要虚拟IP,仅仅让所有服务器节点在Hash环上均匀分布即可达到均匀分布的目的。

至于服务器节点变更(增删)导致的问题,想来应当是无法避开的一个问题,目前看来一致性Hash的解决方案是不断插入新节点,或者删除旧节点。而在完美Hash函数假设的情况下,想来直接将所有服务器节点均匀分配即可,隐隐感觉后者的运算量应该会小很多,但随着服务器节点的数目不断增多,后者带来的运算量减少优势可能就会越来越低,最终相交于一点。往后的话虚拟IP对于均匀性的维护代价就会小于直接将所有物理节点rehash的维护代价。

虽然知道Hash算法应该是无法达到真正意义上的均匀分布,但至少所有的Hash算法应当都是朝着这个方向在努力。

至于目前一致性哈希函数的标准差,大致想法是Hash算法不平均概率之类进行一系列概率相关的运算就可以估算出来。

不过只是个小插曲,研究不了太深,所以目前并没有产生什么实际价值....

package cn.zs.mstpxu;
import static org.hamcrest.CoreMatchers.nullValue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Simple Version
* 本例子仅考虑到单线程执行情况下的ConsistencyHash场景,时间有点仓促所以可能会存在考虑不周的情况,希望大家指正。
* @author Master_PeiXU
*
*/
public class ConsistencyHash {
//保存Server节点映射,方便client查找最近节点。
SortedMap<Integer,String> treeMap = new TreeMap<>();
//保存结果
private Map<String, Integer> consistencyHashResult = new HashMap<String, Integer>();
private static List<String> DEFAULT_LIST = Arrays.asList("Server0","Server1","Server2","Server3","Server4","Server5"
,"Server6","Server7","Server8","Server9");
//Hash环大小
private final int HASH_RANGE = Integer.MAX_VALUE - 1;
private volatile List<String> serverLists = new ArrayList<String>();
private int DEFAULT_VIPCOUNT = 1000;
private int vipCount;
public ConsistencyHash() {
this.serverLists = DEFAULT_LIST;
this.vipCount = DEFAULT_VIPCOUNT;
doHash();
}
public ConsistencyHash(List<String> serverLists,int vipCount) {
this.serverLists = serverLists;
this.vipCount = vipCount;
doHash();
}
/**
* 执行Hash的方法
*/
public void doHash() {
for(String server : serverLists) {
doVirtualIp(vipCount,server);
}
}
/**
* 虚拟IP生成,并插入到Hash环中的方法函数
* @param vipNumber 虚拟Ip个数
* @param server 服务器名称
*/
private void doVirtualIp(int vipNumber, String server) {
for(int i = 0 ; i < vipNumber ; i++) {
String vipTemp = generateVirtualIp(server,i);
Integer value = genHashSlot(vipTemp);
treeMap.put(value,vipTemp);
}
}
/**
* 虚拟Ip生成逻辑,此处简单设为server+0~vipNumber-1
* @param server 真实服务器名称
* @param vipId 虚拟ip节点
* @return
*/
private String generateVirtualIp(String server,int vipId) {
return server + ":" +vipId;
}
/**
* 生成32位Hash
* @param node 节点名称
* @return
*/
private int genHashSlot(String name) {
final int p = 16777619;
int hash = (int)2166136261L;
for (int i = 0; i < name.length(); i++)
hash = (hash ^ name.charAt(i)) * p;
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
// 如果算出来的值为负数则取其绝对值
if (hash < 0)
hash = Math.abs(hash);
return hash;
}
/**
* 在Hash环上增加虚拟服务器
* 本考虑需要维护类中:consistencyHashResult,但该成员变量仅做展示使用,
* 且对其进行动态维护比较复杂,一致性Hash的应用场景可以接受一定程度上的缓存失效;故放弃对该成员变量进行维护。
* @param serverName
* @return
*/
public boolean addServer(String serverName) {
boolean ifSuccess= serverLists.add(serverName);
treeMap.replace(genHashSlot(serverName),serverName);
return ifSuccess;
}
public boolean removeServer(String serverName) {
boolean ifSuccess = serverLists.remove(serverName);
removeServerInHashCycle(serverName);
return ifSuccess;
}
/**
* 在Hash环上删除serverName的所有虚拟节点
* 本考虑需要维护类中:consistencyHashResult,但该成员变量仅做展示使用,
* 且对其进行动态维护比较复杂,一致性Hash的应用场景可以接受一定程度上的缓存失效;故放弃对该成员变量进行维护。
* @param serverName
*/
private void removeServerInHashCycle(String serverName) {
for(int i = 0 ; i < vipCount ; i++ ) {
String virtualServerNode = generateVirtualIp(serverName,i);
Integer hashSlot = genHashSlot(virtualServerNode);
if(virtualServerNode.equals(treeMap.get(hashSlot))) {
treeMap.remove(hashSlot);
}
}
}
/**
*
* @param client 客户端Node
*/
public void recordConsistencyHash(String client){
int clientNode = genHashSlot(client);
//tailMap:Returns a view of the portion of this map whose keys are greater than or equal to clientNode key
//返回大于或等于clientNode的key
SortedMap subMap = treeMap.tailMap(clientNode);
if(subMap.size() != 0) {
//找到第一个大于或等于clientNode的key,符合一致性Hash语义
int firstKey = (Integer)subMap.firstKey();
//获取到的是虚拟Ip节点,本例中写死了分隔方式为以 ":" 进行分隔,很丑。需要修改。
String virtualNode = (String)subMap.get(firstKey);
//获取到了实际节点
String actualNode = virtualNode.split(":")[0];
Integer result = consistencyHashResult.putIfAbsent(actualNode, new Integer(0));
if(null != result) {
consistencyHashResult.put(actualNode, result + 1);
}
}else {//若为空集合,则为边界条件,找TreeMap中的最小节点
firstKey = treeMap.firstKey();
String virtualNode = treeMap.get(firstKey);
//获取到了实际节点
String actualNode = virtualNode.split(":")[0];
Integer result = consistencyHashResult.putIfAbsent(actualNode, new Integer(0));
if(null != result) {
consistencyHashResult.put(actualNode, result + 1);
}
}
}
public static void main(String[] args) {
ConsistencyHash consistencyHash = new ConsistencyHash();
SortedMap<Integer, String> testMap = consistencyHash.treeMap;
for(int i =0 ; i < 1000000 ; i++) {
consistencyHash.recordConsistencyHash(UUID.randomUUID().toString());
}
Set result = consistencyHash.consistencyHashResult.entrySet();
result.stream().forEach(System.out::println);
}
}



用户头像

徐培

关注

还未添加个人签名 2018.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Week 004 作业