一致性HASH算法的JAVA实现

一致性哈希算法是分布式系统中常用的算法。比如,一个分布式的存储系统,要将数据存储到具体的节点上,如果采用普通的hash方法,将数据映射到具体的节点上,如key%N,key是数据的key,N是机器节点数,如果有一个机器加入或退出这个集群,则所有的数据映射都无效了,如果是持久化存储则要做数据迁移,如果是分布式缓存,则其他缓存就失效了。
        一致性Hash算法将 value 映射到一个 32 为的 key 值,也即是 0~2^32-1 次方的数值空间;我们可以将这个空间想象成一个首( 0 )尾( 2^32-1 )相接的圆环。
如下图所示:




Java代码 复制代码  收藏代码
  1.   
  2. package hash;   
  3.   
  4. import java.io.UnsupportedEncodingException;   
  5. import java.nio.ByteBuffer;   
  6. import java.nio.ByteOrder;   
  7. import java.security.MessageDigest;   
  8. import java.security.NoSuchAlgorithmException;   
  9. import java.util.*;   
  10.   
  11. /**  
  12.  * Created by IntelliJ IDEA.  
  13.  * User: test  
  14.  * Date: 12-5-24  
  15.  * Time: 下午5:37  
  16.  * To change this template use File | Settings | File Templates.  
  17.  */  
  18. public class ConsistencyHash {   
  19.     private TreeMap<Long,Object> nodes = null;   
  20.     //真实服务器节点信息   
  21.     private List<Object> shards = new ArrayList();   
  22.     //设置虚拟节点数目   
  23.     private int VIRTUAL_NUM = 4;   
  24.   
  25.     /**  
  26.      * 初始化一致环  
  27.      */  
  28.     public void init() {   
  29.          shards.add("192.168.0.0-服务器0");   
  30.          shards.add("192.168.0.1-服务器1");   
  31.          shards.add("192.168.0.2-服务器2");   
  32.          shards.add("192.168.0.3-服务器3");   
  33.          shards.add("192.168.0.4-服务器4");   
  34.   
  35.         nodes = new TreeMap<Long,Object>();   
  36.         for(int i=0; i<shards.size(); i++) {   
  37.             Object shardInfo = shards.get(i);   
  38.             for(int j=0; j<VIRTUAL_NUM; j++) {   
  39.                 nodes.put(hash(computeMd5("SHARD-" + i + "-NODE-" + j),j), shardInfo);   
  40.             }   
  41.         }   
  42.     }   
  43.   
  44.     /**  
  45.      * 根据key的hash值取得服务器节点信息  
  46.      * @param hash  
  47.      * @return  
  48.      */  
  49.     public Object getShardInfo(long hash) {   
  50.         Long key = hash;   
  51.         SortedMap<Long, Object> tailMap=nodes.tailMap(key);   
  52.         if(tailMap.isEmpty()) {   
  53.             key = nodes.firstKey();   
  54.         } else {   
  55.             key = tailMap.firstKey();   
  56.         }   
  57.         return nodes.get(key);   
  58.     }   
  59.   
  60.     /**  
  61.      * 打印圆环节点数据  
  62.      */  
  63.      public void printMap() {   
  64.          System.out.println(nodes);   
  65.      }   
  66.   
  67.     /**  
  68.      * 根据2^32把节点分布到圆环上面。  
  69.      * @param digest  
  70.      * @param nTime  
  71.      * @return  
  72.      */  
  73.       public long hash(byte[] digest, int nTime) {   
  74.         long rv = ((long) (digest[3+nTime*4] & 0xFF) << 24)   
  75.                 | ((long) (digest[2+nTime*4] & 0xFF) << 16)   
  76.                 | ((long) (digest[1+nTime*4] & 0xFF) << 8)   
  77.                 | (digest[0+nTime*4] & 0xFF);   
  78.   
  79.         return rv & 0xffffffffL; /* Truncate to 32-bits */  
  80.       }   
  81.   
  82.     /**  
  83.      * Get the md5 of the given key.  
  84.      * 计算MD5值  
  85.      */  
  86.      public byte[] computeMd5(String k) {   
  87.         MessageDigest md5;   
  88.         try {   
  89.             md5 = MessageDigest.getInstance("MD5");   
  90.         } catch (NoSuchAlgorithmException e) {   
  91.             throw new RuntimeException("MD5 not supported", e);   
  92.         }   
  93.         md5.reset();   
  94.         byte[] keyBytes = null;   
  95.         try {   
  96.             keyBytes = k.getBytes("UTF-8");   
  97.         } catch (UnsupportedEncodingException e) {   
  98.             throw new RuntimeException("Unknown string :" + k, e);   
  99.         }   
  100.   
  101.         md5.update(keyBytes);   
  102.         return md5.digest();   
  103.      }   
  104.   
  105.      public static void main(String[] args) {   
  106.          Random ran = new Random();   
  107.          ConsistencyHash hash = new ConsistencyHash();   
  108.          hash.init();   
  109.          hash.printMap();   
  110.          //循环50次,是为了取50个数来测试效果,当然也可以用其他任何的数据来测试   
  111.          for(int i=0; i<50; i++) {   
  112.              System.out.println(hash.getShardInfo(hash.hash(hash.computeMd5(String.valueOf(i)),ran.nextInt(hash.VIRTUAL_NUM))));   
  113.          }   
  114.    }   
  115.   
  116. }  

转载自:http://flychao88.iteye.com/blog/1540246

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值