zoukankan      html  css  js  c++  java
  • golang consistent hash 菜鸟分析

    一直找集群的算法,刚好golang上面有一个适合。下面作为菜鸟来分析一下

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. // Copyright (C) 2012 Numerotron Inc.  
    2. // Use of this source code is governed by an MIT-style license  
    3. // that can be found in the LICENSE file.  
    4.   
    5. // Package consistent provides a consistent hashing function.  
    6. //  
    7. // Consistent hashing is often used to distribute requests to a changing set of servers.  For example,  
    8. // say you have some cache servers cacheA, cacheB, and cacheC.  You want to decide which cache server  
    9. // to use to look up information on a user.  
    10. //  
    11. // You could use a typical hash table and hash the user id  
    12. // to one of cacheA, cacheB, or cacheC.  But with a typical hash table, if you add or remove a server,  
    13. // almost all keys will get remapped to different results, which basically could bring your service  
    14. // to a grinding halt while the caches get rebuilt.  
    15. //  
    16. // With a consistent hash, adding or removing a server drastically reduces the number of keys that  
    17. // get remapped.  
    18. //  
    19. // Read more about consistent hashing on wikipedia:  http://en.wikipedia.org/wiki/Consistent_hashing  
    20. //  
    21. package main  
    22.   
    23. import (  
    24.     "errors"  
    25.     "fmt"  
    26.     "hash/crc32"  
    27.     "log"  
    28.     "sort"  
    29.     "strconv"  
    30.     "sync"  
    31. )  
    32.   
    33. type uints []uint32  
    34.   
    35. // Len returns the length of the uints array.  
    36. func (x uints) Len() int { return len(x) }  
    37.   
    38. // Less returns true if element i is less than element j.  
    39. func (x uints) Less(i, j int) bool { return x[i] < x[j] }  
    40.   
    41. // Swap exchanges elements i and j.  
    42. func (x uints) Swap(i, j int) { x[i], x[j] = x[j], x[i] }  
    43.   
    44. // ErrEmptyCircle is the error returned when trying to get an element when nothing has been added to hash.  
    45. var ErrEmptyCircle = errors.New("empty circle")  
    46.   
    47. // Consistent holds the information about the members of the consistent hash circle.  
    48. type Consistent struct {  
    49.     circle           map[uint32]string  
    50.     members          map[string]bool  
    51.     sortedHashes     uints  // 已经排好序的hashes slice , 主要有力搜索 (存储的内容是全部虚拟hashes值)  
    52.     NumberOfReplicas int  
    53.     count            int64  
    54.     scratch          [64]byte  
    55.     sync.RWMutex  
    56. }  
    57.   
    58. // New creates a new Consistent object with a default setting of 20 replicas for each entry.  
    59. //  
    60. // To change the number of replicas, set NumberOfReplicas before adding entries.  
    61. func New() *Consistent {  
    62.     c := new(Consistent)  
    63.     c.NumberOfReplicas = 20  
    64.     c.circle = make(map[uint32]string)  
    65.     c.members = make(map[string]bool)  
    66.     //log.Printf("%p", c)  
    67.     return c  
    68. }  
    69.   
    70. // eltKey generates a string key for an element with an index.  
    71. func (c *Consistent) eltKey(elt string, idx int) string {  
    72.     return elt + "|" + strconv.Itoa(idx)  
    73. }  
    74.   
    75. // Add inserts a string element in the consistent hash.  
    76. func (c *Consistent) Add(elt string) {  
    77.     c.Lock()  
    78.     defer c.Unlock()  
    79.     for i := 0; i < c.NumberOfReplicas; i++ {  
    80.         fmt.Println("i:",i,c.hashKey(c.eltKey(elt, i)))  
    81.         c.circle[c.hashKey(c.eltKey(elt, i))] = elt  
    82.     }  
    83.     //log.Fatal(len(c.circle))  
    84.     //log.Println(len(c.members), elt)  
    85.     c.members[elt] = true  
    86.   
    87.     c.updateSortedHashes()  
    88.     c.count++  
    89. }  
    90.   
    91. // Remove removes an element from the hash.  
    92. func (c *Consistent) Remove(elt string) {  
    93.     c.Lock()  
    94.     defer c.Unlock()  
    95.     for i := 0; i < c.NumberOfReplicas; i++ {  
    96.         delete(c.circle, c.hashKey(c.eltKey(elt, i)))  
    97.     }  
    98.     delete(c.members, elt)  
    99.     c.updateSortedHashes()  
    100.     c.count--  
    101. }  
    102.   
    103. // Set sets all the elements in the hash.  If there are existing elements not present in elts, they will be removed.  
    104. func (c *Consistent) Set(elts []string) {  
    105.     mems := c.Members()  
    106.     for _, k := range mems {  
    107.         found := false  
    108.         for _, v := range elts {  
    109.             if k == v {  
    110.                 found = true  
    111.                 break  
    112.             }  
    113.         }  
    114.         if !found {  
    115.             c.Remove(k)  
    116.         }  
    117.     }  
    118.     for _, v := range elts {  
    119.         c.RLock()  
    120.         _, exists := c.members[v]  
    121.         c.RUnlock()  
    122.         if exists {  
    123.             continue  
    124.         }  
    125.         c.Add(v)  
    126.     }  
    127. }  
    128.   
    129. func (c *Consistent) Members() []string {  
    130.     c.RLock()  
    131.     defer c.RUnlock()  
    132.     var m []string  
    133.     for k := range c.members {  
    134.         m = append(m, k)  
    135.     }  
    136.     return m  
    137. }  
    138.   
    139. // Get returns an element close to where name hashes to in the circle.  
    140. func (c *Consistent) Get(name string) (string, error) {  
    141.     c.RLock()  
    142.     defer c.RUnlock()  
    143.     if len(c.circle) == 0 {  
    144.         return "", ErrEmptyCircle  
    145.     }  
    146.     key := c.hashKey(name)  
    147.     log.Println("need search --> key:",key,"servername:",name)  
    148.     i := c.search(key)  
    149.     fmt.Println(c.sortedHashes[i],c.circle[c.sortedHashes[i]])  
    150.     return c.circle[c.sortedHashes[i]], nil  
    151. }  
    152.   
    153. func (c *Consistent) search(key uint32) (i int) {  
    154.     f := func(x int) bool {  
    155.         log.Println("i",i)  
    156.         // 拿不到相等的  
    157.         return c.sortedHashes[x] > key  
    158.     }  
    159.     i = sort.Search(len(c.sortedHashes), f)  
    160.     log.Println("I:",i)  
    161.     if i >= len(c.sortedHashes) {  
    162.         i = 0  
    163.     }  
    164.     return  
    165. }  
    166.   
    167. // GetTwo returns the two closest distinct elements to the name input in the circle.  
    168. func (c *Consistent) GetTwo(name string) (string, string, error) {  
    169.     c.RLock()  
    170.     defer c.RUnlock()  
    171.     if len(c.circle) == 0 {  
    172.         return "", "", ErrEmptyCircle  
    173.     }  
    174.     //得到hashesw 值  
    175.     key := c.hashKey(name)  
    176.     //搜索hashes  
    177.     i := c.search(key)  
    178.     //获取值  
    179.     a := c.circle[c.sortedHashes[i]]  
    180.     //如果节点只有一个时,直接返回  
    181.     if c.count == 1 {  
    182.         return a, "", nil  
    183.     }  
    184.   
    185.     start := i  
    186.     var b string  
    187.     for i = start + 1; i != start; i++ {  
    188.         if i >= len(c.sortedHashes) {  
    189.             i = 0  
    190.         }  
    191.         b = c.circle[c.sortedHashes[i]]  
    192.         //两个时候否为相同的节点,不是就返回  
    193.         if b != a {  
    194.             break  
    195.         }  
    196.     }  
    197.     return a, b, nil  
    198. }  
    199.   
    200. // GetN returns the N closest distinct elements to the name input in the circle.  
    201. func (c *Consistent) GetN(name string, n int) ([]string, error) {  
    202.     c.RLock()  
    203.     defer c.RUnlock()  
    204.   
    205.     if len(c.circle) == 0 {  
    206.         return nil, ErrEmptyCircle  
    207.     }  
    208.   
    209.     if c.count < int64(n) {  
    210.         n = int(c.count)  
    211.     }  
    212.   
    213.     var (  
    214.         key   = c.hashKey(name)  
    215.         i     = c.search(key)  
    216.         start = i  
    217.         res   = make([]string, 0, n)  
    218.         elem  = c.circle[c.sortedHashes[i]]  
    219.     )  
    220.   
    221.     res = append(res, elem)  
    222.   
    223.     if len(res) == n {  
    224.         return res, nil  
    225.     }  
    226.   
    227.     for i = start + 1; i != start; i++ {  
    228.         if i >= len(c.sortedHashes) {  
    229.             i = 0  
    230.         }  
    231.         elem = c.circle[c.sortedHashes[i]]  
    232.         if !sliceContainsMember(res, elem) {  
    233.             res = append(res, elem)  
    234.         }  
    235.         if len(res) == n {  
    236.             break  
    237.         }  
    238.     }  
    239.   
    240.     return res, nil  
    241. }  
    242.   
    243. func (c *Consistent) hashKey(key string) uint32 {  
    244.     //  
    245.     log.Println("key string:",key)  
    246.     if len(key) < 64 {  
    247.         var scratch [64]byte  
    248.         copy(scratch[:], key)  
    249.         //log.Fatal(len(key), scratch)  
    250.         return crc32.ChecksumIEEE(scratch[:len(key)])  
    251.     }  
    252.     return crc32.ChecksumIEEE([]byte(key))  
    253. }  
    254. // 对hash 进行排序  
    255. func (c *Consistent) updateSortedHashes() {  
    256.     hashes := c.sortedHashes[:0]  
    257.     //reallocate if we're holding on to too much (1/4th)  
    258.     //log.Fatal("exit test:",cap(c.sortedHashes))  
    259.     if cap(c.sortedHashes)/(c.NumberOfReplicas*4) > len(c.circle) {  
    260.         hashes = nil  
    261.     }  
    262.     for k := range c.circle {  
    263.         hashes = append(hashes, k)  
    264.         log.Println(k)  
    265.     }  
    266.     sort.Sort(hashes)  
    267.     c.sortedHashes = hashes  
    268.     log.Println("tem hashes size :",len(hashes),len(c.sortedHashes))  
    269. }  
    270.   
    271. func sliceContainsMember(set []string, member string) bool {  
    272.     for _, m := range set {  
    273.         if m == member {  
    274.             return true  
    275.         }  
    276.     }  
    277.     return false  
    278. }  
    279.   
    280. func main() {  
    281.     c := New()  
    282.     //fmt.Printf("%T", D)  
    283.     c.Add("redis-1")  
    284.     c.Add("redis-2")  
    285.     c.Add("redis-3")  
    286.     log.Fatal(c.GetN("redis-2",1))  
    287.     v, ok := c.Get("redis-one")  
    288.     if ok == nil {  
    289.         for i, vv := range v {  
    290.             fmt.Println(i, vv)  
    291.         }  
    292.     }  
    293.     log.Println("members size:",len(c.members)," circle size :",len(c.circle),"sortHashes:",len(c.sortedHashes),"scratch:",c.scratch)  
    294.     log.Println("sortHashes value:",c.sortedHashes)  
    295.     //log.Fatal("...")  
    296. }  

           其中有几点不是很理解,scratch 这个东西好像没用到,还有就是在计算虚拟节点时,他是使用'>'来计算的,假设我们设置一个节点Redis,那满默认回事redis|1,redis|2..,这样进行节点分布,如果获取redis时,使用redis|1进行搜索,搜索出来就不是redis|1这个虚拟节点了,可能是其他节点。还有在求近距离节点是它是按升排序进行搜索的,而不考虑左右这个方式找最近节点。

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. 1 type Consistent struct {      
    2. 2 »···circle           map[uint32]string // 用来存储node(string) 和 vnode的对应关系,  vnode 是一个hash出来的uint32的整数,也就是最大分区数为4294967296  
    3. 3 »···members          map[string]bool   // string 为 node, bool表示实体节点是否存活  
    4. 4 »···sortedHashes     uints // 已经排好序的hashes slice , 主要有力搜索 (存储的内容是全部vnode hashes值)  
    5. 5 »···NumberOfReplicas int   // node 的权重, 也就是node对应的vnode的个数  
    6. 6 »···count            int64 // 物理节点  
    7. 7 »···scratch          [64]byte  
    8. 8 »···sync.RWMutex  
    9. 9 }               

    这种一致性hash和 Dynamo算法的一致性hash是有很大区别的,这种hash排序不是全有序的;

    测试例子:

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. func main() {  
    2.     c := New()  
    3.     c.Set([]string{"redisA", "redisB"})  
    4.     fmt.Println(c.NumberOfReplicas)  
    5.     fmt.Println(c.Members())  
    6.     for k, v := range c.sortedHashes {  
    7.         fmt.Println(k, c.circle[v])  
    8.     }  
    9. }  


    输出:

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. ▶ go run consistent.go  
    2. 20  
    3. [redisB redisA]  
    4. 0 redisA  
    5. 1 redisB  
    6. 2 redisA  
    7. 3 redisB  
    8. 4 redisA  
    9. 5 redisB  
    10. 6 redisA  
    11. 7 redisB  
    12. 8 redisA  
    13. 9 redisA  
    14. 10 redisB  
    15. 11 redisA  
    16. 12 redisA  
    17. 13 redisB  
    18. 14 redisA  
    19. 15 redisB  
    20. 16 redisB  
    21. 17 redisA  
    22. 18 redisB  
    23. 19 redisB  
    24. 20 redisA  
    25. 21 redisB  
    26. 22 redisA  
    27. 23 redisB  
    28. 24 redisA  
    29. 25 redisB  
    30. 26 redisA  
    31. 27 redisB  
    32. 28 redisA  
    33. 29 redisB  
    34. 30 redisB  
    35. 31 redisA  
    36. 32 redisB  
    37. 33 redisB  
    38. 34 redisA  
    39. 35 redisA  
    40. 36 redisB  
    41. 37 redisA  
    42. 38 redisA  
    43. 39 redisB  



    31 A -> 32B -> 33B ,如果是Dynamo,那么应该是31A -> 32B -> 33A这样循环下去,所以如果想使用这种一致性hash算法来做备份容灾,是不行的。

  • 相关阅读:
    一、linux 挂起进程 nohup
    1.C#窗体和控件
    C#笔记——5.迭代器
    C#笔记——4.集合
    设计模式——3.观察者模式
    设计模式——2.策略模式
    Code基础——1.数据结构
    设计模式——1.模板方法
    C#笔记——3.泛型
    C#笔记——2.委托
  • 原文地址:https://www.cnblogs.com/php-rearch/p/6096665.html
Copyright © 2011-2022 走看看