一直找集群的算法,刚好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. Qt 解析网络数据出现ssl错误

    最近写了点小东西,哈哈, 网络部分是同学帮我搞的 在编译的时候,出现了一下错误 qt.network.ssl: QSslSocket: cannot call unresolved function ...

  2. 验证码 java实现的程序

    makeCheckcode.java package pic; import java.awt.Color; import java.awt.Font; import java.awt.Graphic ...

  3. 给虚拟机发送键盘按键key

    使用举例:virsh send-key 11 KEY_LEFTCTRL KEY_LEFTALT KEY_DELETE作用:发送"ctrl+alt+del"给虚拟机,linux虚拟机 ...

  4. 剑指offer-矩形覆盖10

    题目描述 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形.请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? # -*- coding:utf-8 -*- class S ...

  5. shell及Python爬虫实例展示

    1.shell爬虫实例: [root@db01 ~]# vim pa.sh #!/bin/bash www_link=http://www.cnblogs.com/clsn/default.html? ...

  6. ssh问题_2

    前一段时间配置hadoop集群环境,发现一个现象,教程中的命令形式是ssh hostname,当然这个hostname应该是在ssh发起者的hosts文件中和相应的IP对应:现在问题来了: 我用的是m ...

  7. Hadoop执行bin/stop-all.sh时no namenode to stop异常

    1 先关闭hadoop所有服务 命令: bin/stop-all.sh 2 格式化namenode 命令: bin/hadoop namenode -format 3 重新启动所有服务 命令: bin ...

  8. HL7 2.6 解析(XML)

    用途:检验化验(LIS)实验室设备数据交换解析. using System; using System.Collections.Generic; using System.Text; using Sy ...

  9. DELPHI控件属性事件说明

    常用DELPHI控件属性事件设置说明 常用DELPHI控件属性设置说明目录TForm Class TPanel组件 TToolBar Class TToolButton Class TTimer Cl ...

  10. 【bzoj2049】[Sdoi2008]Cave 洞穴勘测 LCT

    题目描述 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴.假如 ...