一直找集群的算法,刚好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. 「日常训练」Soldier and Badges (CFR304D2B)

    题意 (Codeforces 546B) 问对一个序列最少需要增减几个1能使其彼此不同. 分析 模拟处理.需要注意的是,尽管题目中说了an<=3000,问题是,如果一群a全是3000呢(滑稽), ...

  2. 51单片机数码管字符H自右向左移动

    #include <reg51.h> #define uint unsigned int #define uchar unsigned char sfr P0M0 = 0x94; sfr ...

  3. CSP201412-1:门禁系统

    引言:CSP(http://www.cspro.org/lead/application/ccf/login.jsp)是由中国计算机学会(CCF)发起的“计算机职业资格认证”考试,针对计算机软件开发. ...

  4. 学习bash——环境配置

    一.环境配置文件的重要性 Bash在启动时直接读取这些配置文件,以规划好bash的操作环境. 即使注销bash,我们的设置仍然保存. 二.login shell 通过完整的登录流程取得的bash,称为 ...

  5. c# dll使用注意

    1.dll路径最好不要用到中文,会报:尝试读取或写入受保护的内存.这通常指示其他内存已损坏.

  6. 【Solr】——Solr7安装教程

    前提 solr已经升级7.1,但是我们公司的solr还是使用的4.4,你们说low不low!!!重要的是,人家花费了大气将solr升级,从技术的角度来说solr7比solr4那是翻天覆地的改变! so ...

  7. Win10上部署Apollo配置中心

    基于Docker在Win10上部署Apollo配置中心 https://www.jianshu.com/p/a1215056ce75 http://nobodyiam.com/2016/07/09/i ...

  8. 【bzoj4976】宝石镶嵌 乱搞+dp

    题目描述 从$n$个数中选出$n-k$个,使得它们的二进制或(or)最大.输出这个值. 输入 第一行包含两个正整数$n,k(2\le n\le 100000,1\le k\le 100,k<n) ...

  9. 【bzoj1391】[Ceoi2008]order 网络流最小割

    原文地址:http://www.cnblogs.com/GXZlegend/p/6796937.html 题目描述 有N个工作,M种机器,每种机器你可以租或者买过来. 每个工作包括若干道工序,每道工序 ...

  10. C&C++——标准库

    1.什么是C&C++的标准库? C语言被发明出来时并没有什么库函数,随着C语言的流行,越来越多的厂商或者机构组织开始提供C的编译器,并且同时把经常用到的函数封装成“库”的形式发布:不同的组织发 ...