一致性哈希java实现

该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分配在相同节点上的比例(命中率)。 
多次测试后发现,命中率与结点数目和增减的节点数量有关。同样增删结点数目情况下,结点多时命中率高。同样节点数目,增删结点越少,命中率越高。这些都与实际情况相符。 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值