zoukankan      html  css  js  c++  java
  • [转载] 应用于负载均衡的一致性哈希及java实现

    转载自http://blog.csdn.net/haitao111313/article/details/7537799

    这几天看了几遍一致性哈希的文章,但是都没有比较完整的实现,因此试着实现了一下,这里我就不讲一致性哈希的原理了,网上很多,以一致性哈希用在负载均衡的实例来说,一致性哈希就是先把主机ip从小大到全部放到一个环内,然后客户端ip来连接的时候,把客户端ip连接到大小最接近客户端ip且大于客户端ip的主机。当然,这里的ip一般都是要先hash一下的。我的程序运行结果如下:

        

    1. 添加客户端,一开始有4个主机,分别为s1,s2,s3,s4,每个主机有100个虚拟主机:  
    2. 101客户端(hash:-3872430075274208315)连接到主机->s2-192.168.1.2  
    3. 102客户端(hash:-6461488502093916753)连接到主机->s1-192.168.1.1  
    4. 103客户端(hash:-3272337528088901176)连接到主机->s3-192.168.1.3  
    5. 104客户端(hash:7274050343425899995)连接到主机->s2-192.168.1.2  
    6. 105客户端(hash:6218187750346216421)连接到主机->s1-192.168.1.1  
    7. 106客户端(hash:-8497989778066313989)连接到主机->s2-192.168.1.2  
    8. 107客户端(hash:2219601794372203979)连接到主机->s3-192.168.1.3  
    9. 108客户端(hash:1903054837754071260)连接到主机->s3-192.168.1.3  
    10. 109客户端(hash:-2425484502654523425)连接到主机->s1-192.168.1.1  
    11. 删除主机s2-192.168.1.2的变化:  
    12. hash(-8497989778066313989)改变到->s4-192.168.1.4  
    13. hash(7274050343425899995)改变到->s2-192.168.1.2  
    14. hash(-3872430075274208315)改变到->s4-192.168.1.4  
    15. hash(7274050343425899995)改变到->s1-192.168.1.1  
    16. 增加主机s5-192.168.1.5的变化:  
    17. hash(1903054837754071260)改变到->s5-192.168.1.5  
    18. hash(1903054837754071260)改变到->s5-192.168.1.5  
    19. hash(-3272337528088901176)改变到->s5-192.168.1.5  
    20. 最后的客户端到主机的映射为:  
    21. hash(-8497989778066313989)连接到主机->s4-192.168.1.4  
    22. hash(-6461488502093916753)连接到主机->s1-192.168.1.1  
    23. hash(-3872430075274208315)连接到主机->s4-192.168.1.4  
    24. hash(-3272337528088901176)连接到主机->s5-192.168.1.5  
    25. hash(-2425484502654523425)连接到主机->s1-192.168.1.1  
    26. hash(1903054837754071260)连接到主机->s5-192.168.1.5  
    27. hash(2219601794372203979)连接到主机->s3-192.168.1.3  
    28. hash(6218187750346216421)连接到主机->s1-192.168.1.1  
    29. hash(7274050343425899995)连接到主机->s1-192.168.1.1  

    看结果可知:一开始添加到9个客户端,连接到主机s1,s2,s3,s4的客户端分别有3,3,3,0个,经过删除主机s2,添加主机s5,最后9个客户端分别连接到主机s1,s2,s3,s4,s5的个数为4,0,1,2,2.这里要说明一下删除主机s2的情况,hash尾号为9995的客户端先连接到s2,再连接到s1,为什么会出现这种情况呢?因为每一个真实主机有n个虚拟主机,删除s2却打印“hash(7274050343425899995)改变到->s2-192.168.1.2”是因为删除了s2的其中一个虚拟主机,跳转到另一个虚拟主机,但还是在s2上,当然,这里是打印中间情况,以便了解,真实的环境是删除了s2后,所有他的虚拟节点都会马上被删除,虚拟节点上的连接也会重新连接到另一个主机的虚拟节点,不会存在这种中间情况。

    以下给出所有的实现代码,大家共同学习:

    1. public class Shard<Node> { // S类封装了机器节点的信息 ,如name、password、ip、port等  
    2.   
    3.     static private TreeMap<Long, Node> nodes; // 虚拟节点到真实节点的映射  
    4.     static private TreeMap<Long,Node> treeKey; //key到真实节点的映射  
    5.     static private List<Node> shards = new ArrayList<Node>(); // 真实机器节点  
    6.     private final int NODE_NUM = 100; // 每个机器节点关联的虚拟节点个数  
    7.     boolean flag = false;  
    8.       
    9.     public Shard(List<Node> shards) {  
    10.         super();  
    11.         this.shards = shards;  
    12.         init();  
    13.     }  
    14.   
    15.     public static void main(String[] args) {  
    16. //      System.out.println(hash("w222o1d"));  
    17. //      System.out.println(Long.MIN_VALUE);  
    18. //      System.out.println(Long.MAX_VALUE);  
    19.         Node s1 = new Node("s1", "192.168.1.1");  
    20.         Node s2 = new Node("s2", "192.168.1.2");  
    21.         Node s3 = new Node("s3", "192.168.1.3");  
    22.         Node s4 = new Node("s4", "192.168.1.4");  
    23.         Node s5 = new Node("s5","192.168.1.5");  
    24.         shards.add(s1);  
    25.         shards.add(s2);  
    26.         shards.add(s3);  
    27.         shards.add(s4);  
    28.         Shard<Node> sh = new Shard<Shard.Node>(shards);  
    29.         System.out.println("添加客户端,一开始有4个主机,分别为s1,s2,s3,s4,每个主机有100个虚拟主机:");  
    30.         sh.keyToNode("101客户端");  
    31.         sh.keyToNode("102客户端");  
    32.         sh.keyToNode("103客户端");  
    33.         sh.keyToNode("104客户端");  
    34.         sh.keyToNode("105客户端");  
    35.         sh.keyToNode("106客户端");  
    36.         sh.keyToNode("107客户端");  
    37.         sh.keyToNode("108客户端");  
    38.         sh.keyToNode("109客户端");  
    39.           
    40.         sh.deleteS(s2);  
    41.           
    42.           
    43.         sh.addS(s5);  
    44.           
    45.         System.out.println("最后的客户端到主机的映射为:");  
    46.         printKeyTree();  
    47.     }  
    48.     public static void printKeyTree(){  
    49.         for(Iterator<Long> it = treeKey.keySet().iterator();it.hasNext();){  
    50.             Long lo = it.next();  
    51.             System.out.println("hash("+lo+")连接到主机->"+treeKey.get(lo));  
    52.         }  
    53.           
    54.     }  
    55.       
    56.     private void init() { // 初始化一致性hash环  
    57.         nodes = new TreeMap<Long, Node>();  
    58.         treeKey = new TreeMap<Long, Node>();  
    59.         for (int i = 0; i != shards.size(); ++i) { // 每个真实机器节点都需要关联虚拟节点  
    60.             final Node shardInfo = shards.get(i);  
    61.   
    62.             for (int n = 0; n < NODE_NUM; n++)  
    63.                 // 一个真实机器节点关联NODE_NUM个虚拟节点  
    64.                 nodes.put(hash("SHARD-" + shardInfo.name + "-NODE-" + n), shardInfo);  
    65.         }  
    66.     }  
    67.     //增加一个主机  
    68.     private void addS(Node s) {  
    69.         System.out.println("增加主机"+s+"的变化:");  
    70.         for (int n = 0; n < NODE_NUM; n++)  
    71.             addS(hash("SHARD-" + s.name + "-NODE-" + n), s);  
    72.   
    73.     }  
    74.       
    75.     //添加一个虚拟节点进环形结构,lg为虚拟节点的hash值  
    76.     public void addS(Long lg,Node s){  
    77.         SortedMap<Long, Node> tail = nodes.tailMap(lg);  
    78.         SortedMap<Long,Node>  head = nodes.headMap(lg);  
    79.         Long begin = 0L;  
    80.         Long end = 0L;  
    81.         SortedMap<Long, Node> between;  
    82.         if(head.size()==0){  
    83.             between = treeKey.tailMap(nodes.lastKey());  
    84.             flag = true;  
    85.         }else{  
    86.             begin = head.lastKey();  
    87.             between = treeKey.subMap(begin, lg);  
    88.             flag = false;  
    89.         }  
    90.         nodes.put(lg, s);  
    91.         for(Iterator<Long> it=between.keySet().iterator();it.hasNext();){  
    92.             Long lo = it.next();  
    93.             if(flag){  
    94.                 treeKey.put(lo, nodes.get(lg));  
    95.                 System.out.println("hash("+lo+")改变到->"+tail.get(tail.firstKey()));  
    96.             }else{  
    97.                 treeKey.put(lo, nodes.get(lg));  
    98.                 System.out.println("hash("+lo+")改变到->"+tail.get(tail.firstKey()));  
    99.             }  
    100.         }  
    101.     }  
    102.       
    103.     //删除真实节点是s  
    104.     public void deleteS(Node s){  
    105.         if(s==null){  
    106.             return;  
    107.         }  
    108.         System.out.println("删除主机"+s+"的变化:");      
    109.         for(int i=0;i<NODE_NUM;i++){  
    110.             //定位s节点的第i的虚拟节点的位置  
    111.             SortedMap<Long, Node> tail = nodes.tailMap(hash("SHARD-" + s.name + "-NODE-" + i));  
    112.             SortedMap<Long,Node>  head = nodes.headMap(hash("SHARD-" + s.name + "-NODE-" + i));  
    113.             Long begin = 0L;  
    114.             Long end = 0L;  
    115.               
    116.             SortedMap<Long, Node> between;  
    117.             if(head.size()==0){  
    118.                 between = treeKey.tailMap(nodes.lastKey());  
    119.                 end = tail.firstKey();  
    120.                 tail.remove(tail.firstKey());  
    121.                 nodes.remove(tail.firstKey());//从nodes中删除s节点的第i个虚拟节点  
    122.                 flag = true;  
    123.             }else{  
    124.                 begin = head.lastKey();  
    125.                 end = tail.firstKey();  
    126.                 tail.remove(tail.firstKey());  
    127.                 between = treeKey.subMap(begin, end);//在s节点的第i个虚拟节点的所有key的集合  
    128.                 flag = false;  
    129.             }  
    130.             for(Iterator<Long> it = between.keySet().iterator();it.hasNext();){  
    131.                 Long lo  = it.next();  
    132.                 if(flag){  
    133.                     treeKey.put(lo, tail.get(tail.firstKey()));  
    134.                     System.out.println("hash("+lo+")改变到->"+tail.get(tail.firstKey()));  
    135.                 }else{  
    136.                     treeKey.put(lo, tail.get(tail.firstKey()));  
    137.                     System.out.println("hash("+lo+")改变到->"+tail.get(tail.firstKey()));  
    138.                 }  
    139.             }  
    140.         }  
    141.           
    142.     }  
    143.   
    144.     //映射key到真实节点  
    145.     public void keyToNode(String key){  
    146.         SortedMap<Long, Node> tail = nodes.tailMap(hash(key)); // 沿环的顺时针找到一个虚拟节点  
    147.         if (tail.size() == 0) {  
    148.             return;  
    149.         }  
    150.         treeKey.put(hash(key), tail.get(tail.firstKey()));  
    151.         System.out.println(key+"(hash:"+hash(key)+")连接到主机->"+tail.get(tail.firstKey()));  
    152.     }  
    153.       
    154.     /** 
    155.      *  MurMurHash算法,是非加密HASH算法,性能很高, 
    156.      *  比传统的CRC32,MD5,SHA-1(这两个算法都是加密HASH算法,复杂度本身就很高,带来的性能上的损害也不可避免) 
    157.      *  等HASH算法要快很多,而且据说这个算法的碰撞率很低. 
    158.      *  http://murmurhash.googlepages.com/ 
    159.      */  
    160.     private static Long hash(String key) {  
    161.           
    162.         ByteBuffer buf = ByteBuffer.wrap(key.getBytes());  
    163.         int seed = 0x1234ABCD;  
    164.           
    165.         ByteOrder byteOrder = buf.order();  
    166.         buf.order(ByteOrder.LITTLE_ENDIAN);  
    167.   
    168.         long m = 0xc6a4a7935bd1e995L;  
    169.         int r = 47;  
    170.   
    171.         long h = seed ^ (buf.remaining() * m);  
    172.   
    173.         long k;  
    174.         while (buf.remaining() >= 8) {  
    175.             k = buf.getLong();  
    176.   
    177.             k *= m;  
    178.             k ^= k >>> r;  
    179.             k *= m;  
    180.   
    181.             h ^= k;  
    182.             h *= m;  
    183.         }  
    184.   
    185.         if (buf.remaining() > 0) {  
    186.             ByteBuffer finish = ByteBuffer.allocate(8).order(  
    187.                     ByteOrder.LITTLE_ENDIAN);  
    188.             // for big-endian version, do this first:  
    189.             // finish.position(8-buf.remaining());  
    190.             finish.put(buf).rewind();  
    191.             h ^= finish.getLong();  
    192.             h *= m;  
    193.         }  
    194.   
    195.         h ^= h >>> r;  
    196.         h *= m;  
    197.         h ^= h >>> r;  
    198.   
    199.         buf.order(byteOrder);  
    200.         return h;  
    201.     }  
    202.       
    203.     static class Node{  
    204.         String name;  
    205.         String ip;  
    206.         public Node(String name,String ip) {  
    207.             this.name = name;  
    208.             this.ip = ip;  
    209.         }  
    210.         @Override  
    211.         public String toString() {  
    212.             return this.name+"-"+this.ip;  
    213.         }  
    214.     }  
    215.   
    216. }  


    参考:http://blog.csdn.net/wuhuan_wp/article/details/7010071

  • 相关阅读:
    257. Binary Tree Paths
    324. Wiggle Sort II
    315. Count of Smaller Numbers After Self
    350. Intersection of Two Arrays II
    295. Find Median from Data Stream
    289. Game of Life
    287. Find the Duplicate Number
    279. Perfect Squares
    384. Shuffle an Array
    E
  • 原文地址:https://www.cnblogs.com/scott19820130/p/4931905.html
Copyright © 2011-2022 走看看