一致性哈希java实现

来源:互联网 发布:js禁止文本框输入 编辑:程序博客网 时间:2024/05/16 11:20

该client采用TreeMap存储所有节点,模拟一个环形的逻辑关系。在这个环中,节点之前是存在顺序关系的,所以TreeMap的key必须实现Comparator接口。 
那节点是怎样放入这个环中的呢? 

Java代码  

       //对所有节点,生成nCopies个虚拟结点           for(Node node : nodes) {               //每四个虚拟结点为一组,为什么这样?下面会说到               for(int i=0; i<nCopies / 4; i++) {                   //getKeyForNode方法为这组虚拟结点得到惟一名称                   byte[] digest=HashAlgorithm.computeMd5(getKeyForNode(node, i));               /** Md5是一个16字节长度的数组,将16字节的数组每四个字节一组,                          分别对应一个虚拟结点,这就是为什么上面把虚拟结点四个划分一组的原因*/                   for(int h=0;h<4;h++) {                     //对于每四个字节,组成一个long值数值,做为这个虚拟节点的在环中的惟一key                       Long k = ((long)(digest[3+h*4]&0xFF) << 24)                           | ((long)(digest[2+h*4]&0xFF) << 16)                           | ((long)(digest[1+h*4]&0xFF) << 8)                           | (digest[h*4]&0xFF);                                              allNodes.put(k, node);                   }               }           }  



上面的流程大概可以这样归纳:四个虚拟结点为一组,以getKeyForNode方法得到这组虚拟节点的name,Md5编码后,每个虚拟结点对应Md5码16个字节中的4个,组成一个long型数值,做为这个虚拟结点在环中的惟一key。第12行k为什么是Long型的呢?呵呵,就是因为Long型实现了Comparator接口。 

处理完正式结点在环上的分布后,可以开始key在环上寻找节点的游戏了。 
对于每个key还是得完成上面的步骤:计算出Md5,根据Md5的字节数组,通过Kemata Hash算法得到key在这个环中的位置。 

Java代码  

  final Node rv;           byte[] digest = hashAlg.computeMd5(keyValue);           Long key = hashAlg.hash(digest, 0);           //如果找到这个节点,直接取节点,返回           if(!ketamaNodes.containsKey(key)) {           //得到大于当前key的那个子Map,然后从中取出第一个key,就是大于且离它最近的那个key               SortedMap<Long, Node> tailMap=ketamaNodes.tailMap(key);               if(tailMap.isEmpty()) {                   key=ketamaNodes.firstKey();               } else {                   key=tailMap.firstKey();               }               //在JDK1.6中,ceilingKey方法可以返回大于且离它最近的那个key               //For JDK1.6 version   //          key = ketamaNodes.ceilingKey(key);   //          if (key == null) {   //              key = ketamaNodes.firstKey();   //          }           }                                 rv=allNodes.get(key);  



引文中已详细描述过这种取节点逻辑:在环上顺时针查找,如果找到某个节点,就返回那个节点;如果没有找到,则取整个环的第一个节点。 

测试结果 
测试代码是自己整理的,主体方法没有变 

分布平均性测试:测试随机生成的众多key是否会平均分布到各个结点上 
测试结果如下: 

Java代码  

1. Nodes count : 5, Keys count : 100000, Normal percent : 20.0%  

2. -------------------- boundary  ----------------------  

3. Node name :node1 - Times : 20821 - Percent : 20.821001%  

4. Node name :node3 - Times : 19018 - Percent : 19.018%  

5. Node name :node5 - Times : 19726 - Percent : 19.726%  

6. Node name :node2 - Times : 19919 - Percent : 19.919%  

7. Node name :node4 - Times : 20516 - Percent : 20.516%  


最上面一行是参数说明,节点数目,总共有多少key,每个节点应该分配key的比例是多少。下面是每个结点分配到key的数目和比例。 
多次测试后发现,这个Hash算法的节点分布还是不错的,都在标准比例左右徘徊,是个合适的负载均衡算法。 


节点增删测试:在环上插入N个结点,每个节点nCopies个虚拟结点。随机生成众多key,在增删节点时,测试同一个key选择相同节点的概率 
测试如果如下: 

Java代码  

1. Normal case : nodes count : 50  

2. Added case : nodes count : 51  

3. Reduced case : nodes count : 49  

4. ------------ boundary -------------  

5. Same percent in added case : 93.765%  

6. Same percent in reduced case : 93.845%  


上面三行分别是正常情况,节点增加,节点删除情况下的节点数目。下面两行表示在节点增加和删除情况下,同一个key分配在相同节点上的比例(命中率)。 
多次测试后发现,命中率与结点数目和增减的节点数量有关。同样增删结点数目情况下,结点多时命中率高。同样节点数目,增删结点越少,命中率越高。这些都与实际情况相符。 

 

0 0
原创粉丝点击