Week 004 作业
第五周的作业是写一个一致性哈希的算法并验证,只写了大概思路的架子。还差一段距离。
正在加紧调试中......
##直接贴代码了....其实并不能用,只通过注释大概写了下思路.....今晚到明天尽量调试成功再更新:
目前更新好了,代码简单测试可用,但性能似乎有低下,且没有更专业的测试,太晚了,明天排查下为什么那么慢。
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 加入
还未添加个人简介
评论