一直找集群的算法,刚好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. Python 3基础教程18-获取用户键盘输入

    有时候,我们需要获取用户的键盘输入的信息,然后得到信息,拿去做一些事情. 请看下面的demo.py # 练习如何通过键盘获取用户输入 x = input('What is your name?') p ...

  2. golang模拟新浪微博登录

    1.基于幽灵蛛pholcus开源项目的规则 直接贴代码,代码可以更改后用于其他爬虫项目 package pholcus_lib // 基础包 import ( // "github.com/ ...

  3. [推荐]spring cloud 详解

    http://blog.csdn.net/column/details/15197.html

  4. 【CodeForces】9A-Die Roll

    目录 Question Solution 解法1 解法2 Question 三个人掷骰子,前两个人的得分分别是Y和W,问第三个人胜利的概率(第三个人得分不小于Y.W)?结果输出格式为\(A/B\),如 ...

  5. BZOJ 4031 HEOI2015 小Z的房间 基尔霍夫矩阵+行列式+高斯消元 (附带行列式小结)

    原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4031 Description 你突然有了一个大房子,房子里面有一些房间.事实上,你的房子可 ...

  6. Android stateMachine分析

    StateMachine与State模式的详细介绍可以参考文章:Android学习 StateMachine与State模式 下面是我对于StateMachine的理解: 先了解下消息处理.看下Sta ...

  7. 将EXCEL表中的数据轻松导入Mysql数据表

    转载自:http://blog.163.com/dielianjun@126/blog/static/164250113201042310181431/ 在网络上有不较多的方法,在此介绍我已经验证的方 ...

  8. 微信公众号开发java框架:wx4j(MaterialUtils篇)

    wx4j-MaterialUtils的使用 函数说明:上传永久视频素材 参数:文件路径.视频描述(通过setter填充内容即可) 返回值:微信服务器返回的json字符串 public static S ...

  9. 【PHP】- include、require、include_once 和 require_once的区别

    1.include:会将指定的档案读入并且执行里面的程序.      被导入的档案中的程序代码都会被执行,而且这些程序在执行的时候会拥有和源文件中呼叫到 include() 函数的位置相同的变量范围( ...

  10. 使用 window.getSelection() 方法获取鼠标划取部分的起始位置和结束位置的问题(高亮后不能正确获取)

    如果没有高亮等复杂处理,只需要获取一段文字中选取的字和位置,那么 使用window.getSelection()获取div中选中文字内容及位置 怎么获取textarea中选中文字 则可以满足需求: - ...