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

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

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

  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排序不是全有序的;

测试例子:

  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. }

输出:

  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算法来做备份容灾,是不行的。

golang consistent hash 菜鸟分析的更多相关文章

  1. consistent hash(一致性哈希算法)

    一.产生背景 今天咱不去长篇大论特别详细地讲解consistent hash,我争取用最轻松的方式告诉你consistent hash算法是什么,如果需要深入,Google一下~. 举个栗子吧: 比如 ...

  2. 一文了解 Consistent Hash

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/LGLqEOlGExKob8xEXXWckQ作者:钱幸川 在分布式环境下面,我们经常会通过一定的 ...

  3. golang 性能调优分析工具 pprof(下)

    golang 性能调优分析工具 pprof(上)篇, 这是下篇. 四.net/http/pprof 4.1 代码例子 1 go version go1.13.9 把上面的程序例子稍微改动下,命名为 d ...

  4. Nginx的负载均衡 - 一致性哈希 (Consistent Hash)

    Nginx版本:1.9.1 我的博客:http://blog.csdn.net/zhangskd 算法介绍 当后端是缓存服务器时,经常使用一致性哈希算法来进行负载均衡. 使用一致性哈希的好处在于,增减 ...

  5. 一致性Hash算法(Consistent Hash)

    分布式算法 在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括: 轮循算法(Round Robin).哈希算法(HASH).最少连接算法(Least Connection).响应速度算法(Re ...

  6. golang thrift 源码分析,服务器和客户端究竟是如何工作的

    首先编写thrift文件(rpcserver.thrift),运行thrift --gen go rpcserver.thrift,生成代码 namespace go rpc service RpcS ...

  7. golang (5) http 请求分析

    http 分析包分析 fmt.Println("get Content-Type: ", r.Header.Get("Content-Type")) var r ...

  8. oralce之 10046对Hash Join分析

    前两天解决了一个优化SQL的case,SQL语句如下,big_table为150G大小,small_table很小,9000多条记录,不到1M大小,hash_area_size, sort_area_ ...

  9. 【go】继续go go go,ubuntu环境搭建及golang的依赖关系分析

    这次是在ubuntu14.04 amd64上搭建go的编译环境,使用的IDE换成了sublime text,具体步骤参照的是 http://blog.csdn.net/aqiang912/articl ...

随机推荐

  1. 3、shader深度测试(Cull、ZWrite 、ZTest )

    剔除和深度测试是渲染法线中的一个流程 Cull:默认情况下,Unity中给的所有Shader都是单面的,它都把反面的渲染给关闭掉了,如果你在开发的过程中需要使用到双面,只要把cull关闭(off)即可 ...

  2. TP5 急速上手 语法规则

    Tp5  规则 命名规范 目录和文件名采用‘小写+下划线’,并且以小写字母开头: 类库.函数文件统一以.php为后缀: 类的文件名均以命名空间定义,并且命名空间的路径和类库文件所在路径一致(包括大小写 ...

  3. 10-Mysql数据库----数据的增删改

    本节重点: 插入数据 INSERT 更新数据 UPDATE 删除数据 DELETE 再来回顾一下之前我们练过的一些操作,相信大家都对插入数据.更新数据.删除数据有了全面的认识.那么在mysql中其实最 ...

  4. python安装Django

    现在有很多建站系统,很多都是基于php的,比如WordPress. 而Django 是老牌基于Python的CMS框架了,一直听说很强大,甚至曾经很红的Ruby On Rails都参考了它的很多概念, ...

  5. POJ 3858 Hurry Plotter(DP)

    Description A plotter is a vector graphics printing device that connects to a computer to print grap ...

  6. 数论3——gcd&&lcm

    gcd(a, b),就是求a和b的最大公约数 lcm(a, b),就是求a和b的最小公倍数 然后有个公式 a*b = gcd * lcm     ( gcd就是gcd(a, b), ( •̀∀•́ ) ...

  7. java多线程二之线程同步的三种方法

          java多线程的难点是在:处理多个线程同步与并发运行时线程间的通信问题.java在处理线程同步时,常用方法有: 1.synchronized关键字. 2.Lock显示加锁. 3.信号量Se ...

  8. java获取本机器的IP(linux和windows)

    目录 描述 方案描述 获取Windows下的IP 获取linux下的IP 判断操作系统的类型 最后将上面三个方法进行整合 参考 描述 由于项目是部署在集群上的,需要项目能够自动采集各机器的信息.jav ...

  9. C#怎么调用方法

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Exep ...

  10. gdb调试命令的使用及总结

    gdb调试命令的使用及总结 gdb是一个在UNIX环境下的命令行调试工具.如果需要使用gdb调试程序,请在gcc时加上-g选项.下面的命令部分是简化版,比如使用l代替list等等. 1.基本命令 命令 ...