1. 程式人生 > >一致性hash演算法 java程式碼實現與測試

一致性hash演算法 java程式碼實現與測試

轉載:http://blog.csdn.net/pcceo1/article/details/51493934

寫了一個一致性hash的Java實現程式碼,演算法是用別人的,據說很好,然後自己做了一個測試,用執行緒池起了1000個執行緒,每個執行緒hash10000次,模擬一萬次資料hash,並將測試結果上傳。

  1. /** 
  2.  * 一致性hash程式碼 
  3.  *  
  4.  * @author shiguiming 
  5.  * 
  6.  * @param <T> 
  7.  */
  8. publicclass Shared<T> {  
  9.     // 真實節點對應的虛擬節點數量
  10.     privateint length = 100
    ;  
  11.     // 虛擬節點資訊
  12.     private TreeMap<Long, T> virtualNodes;  
  13.     // 真實節點資訊
  14.     private List<T> realNodes;  
  15.     public Shared(List<T> realNodes) {  
  16.         this.realNodes = realNodes;  
  17.         init();  
  18.     }  
  19.     public List<T> getReal() {  
  20.         return realNodes;  
  21.     }  
  22.     /** 
  23.      * 初始化虛擬節點 
  24.      */
  25.     privatevoid init() {  
  26.         virtualNodes = new TreeMap<Long, T>();  
  27.         for (int i = 0; i < realNodes.size(); i++) {  
  28.             for (int j = 0; j < length; j++) {  
  29.                 virtualNodes.put(hash("aa" + i + j), realNodes.get(i));  
  30.             }  
  31.         }  
  32.     }  
  33.     /** 
  34.      * 獲取一個結點 
  35.      *  
  36.      * @param key 
  37.      * @return 
  38.      */
  39.     @SuppressWarnings("unchecked")  
  40.     public T getNode(String key) {  
  41.         Long hashedKey = hash(key);  
  42.         // TODO judge null
  43.         Entry en = virtualNodes.ceilingEntry(hashedKey);  
  44.         if (en == null) {  
  45.             return (T) virtualNodes.firstEntry().getValue();  
  46.         }  
  47.         return (T) en.getValue();  
  48.     }  
  49.     /** 
  50.      * MurMurHash演算法,是非加密HASH演算法,效能很高, 
  51.      * 比傳統的CRC32,MD5,SHA-1(這兩個演算法都是加密HASH演算法,複雜度本身就很高,帶來的效能上的損害也不可避免) 
  52.      * 等HASH演算法要快很多,而且據說這個演算法的碰撞率很低. http://murmurhash.googlepages.com/ 
  53.      */
  54.     private Long hash(String key) {  
  55.         ByteBuffer buf = ByteBuffer.wrap(key.getBytes());  
  56.         int seed = 0x1234ABCD;  
  57.         ByteOrder byteOrder = buf.order();  
  58.         buf.order(ByteOrder.LITTLE_ENDIAN);  
  59.         long m = 0xc6a4a7935bd1e995L;  
  60.         int r = 47;  
  61.         long h = seed ^ (buf.remaining() * m);  
  62.         long k;  
  63.         while (buf.remaining() >= 8) {  
  64.             k = buf.getLong();  
  65.             k *= m;  
  66.             k ^= k >>> r;  
  67.             k *= m;  
  68.             h ^= k;  
  69.             h *= m;  
  70.         }  
  71.         if (buf.remaining() > 0) {  
  72.             ByteBuffer finish = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);  
  73.             // for big-endian version, do this first:
  74.             // finish.position(8-buf.remaining());
  75.             finish.put(buf).rewind();  
  76.             h ^= finish.getLong();  
  77.             h *= m;  
  78.         }  
  79.         h ^= h >>> r;  
  80.         h *= m;  
  81.         h ^= h >>> r;  
  82.         buf.order(byteOrder);  
  83.         return h;  
  84.     }  
  85.     /** 
  86.      * 測試內部類 
  87.      *  
  88.      * @author shiguiming 
  89.      * 
  90.      */
  91.     staticpublicclass Node {  
  92.         privateint name;  
  93.         privateint count = 0;  
  94.         public Node() {  
  95.         }  
  96.         public Node(int i) {  
  97.             this.name = i;  
  98.         }  
  99.         publicint getName() {  
  100.             return name;  
  101.         }  
  102.         publicvoid setName(int name) {  
  103.             this.name = name;  
  104.         }  
  105.         publicint getCount() {  
  106.             return count;  
  107.         }  
  108.         // 同步方法,防止併發
  109.         synchronizedpublicvoid inc() {  
  110.             count++;  
  111.         }  
  112.     }  
  113.     /** 
  114.      * 測試方法 
  115.      *  
  116.      * @param args 
  117.      * @throws InterruptedException 
  118.      */
  119.     publicstaticvoid main(String[] args) throws InterruptedException {  
  120.         List<Node> ndList = new ArrayList<Node>();  
  121.         int i = 0;  
  122.         while (true) {  
  123.             ndList.add(new Node(i));  
  124.             if (i++ == 9)  
  125.                 break;  
  126.         }  
  127.         final Shared<Node> sh = new Shared<Node>(ndList);  
  128.         ExecutorService es = Executors.newCachedThreadPool();  
  129.         final CountDownLatch cdl = new CountDownLatch(1000);  
  130.         // 1000個執行緒
  131.         for (int j = 0; j < 1000; j++) {  
  132.             es.execute(new Runnable() {  
  133.                 @Override
  134.                 publicvoid run() {  
  135.                     // Random rd = new Random(1100);
  136.                     for (int k = 0; k < 10000; k++) {  
  137.                         sh.getNode(String.valueOf(Math.random())).inc();  
  138.                     }  
  139.                     cdl.countDown();  
  140.                 }  
  141.             });  
  142.         }  
  143.         // 等待所有執行緒結束
  144.         cdl.await();  
  145.         List<Node> nodeList = sh.getReal();  
  146.         for (Node node : nodeList) {  
  147.             System.out.println("node" + node.getName() + ":" + node.getCount());  
  148.         }  
  149.     }  
  150. }  

測試結果:


一共10,000,000次 hash,基本算是較均勻投遞到10個節點,有什麼問題大家交流~