一直找集群的算法,刚好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-学习-import语句导入模块

    简单的学习一下调用外部的模块文件. 在Python中,模块是一种组织形式,它将彼此有关系的Pyrhon 代码组织到一个个独立的文件当中,模块可以包含可执行代码,函数,和类或者是这些东西的组合. 当我们 ...

  2. C++学习008-delete与delete[]的差别

    对于简单的数据类型,delete与delete[]是没啥差别的,就是等价的 例如 int main() { int *pdata = new int[20]; delete pdata; //dele ...

  3. 孤荷凌寒自学python第七十五天开始写Python的第一个爬虫5

    孤荷凌寒自学python第七十五天开始写Python的第一个爬虫5 (完整学习过程屏幕记录视频地址在文末) 今天在上一天的基础上继续完成对我的第一个代码程序的书写. 直接上代码.详细过程见文末屏幕录像 ...

  4. ubuntu中执行truffle build出现问题

    进行build之前,采用默认构建器方式创建客户端,先安装默认构建器: npm install truffle-default-builder --save 然后需要修改truffle.js配置文件如下 ...

  5. Introduction to TCP/IP

    目录 First Week DHCP 子网掩码 ip路由表 Second Week ipv4 ipv6 TCP和UDP Third Week NAT RPC FTP E-mail Fouth Week ...

  6. java计算两个日期之间的相隔天数

    [原创] 之前在很多竞赛的题目中有这样饿一类题,计算给定两个日期之间的相隔天数,或者很多类似的题目,都是需要转化到这个问题上时,之前用c++写的时候思想是这样的,一个结构体,包括年月日,还有一个计算下 ...

  7. const在c/c++中的区别

    #include <iostream> using namespace std; int main() { ; ; }; ; i < sizeof array / sizeof *a ...

  8. Hash表 算法的详细解析

    http://xingyunbaijunwei.blog.163.com/blog/static/76538067201111494524190/ 什么是HashHash,一般翻译做“散列”,也有直接 ...

  9. 正则awk和查看文件行数

    [root@WebServer aa]# cat oldboy.txt I am oldboy myqq is 49000448[root@WebServer aa]# cat oldboy.txt ...

  10. 使用emit发出信号

    1. 信号声明 在发送信号的模块类头文件中声明信号函数 signals: void sendRate(QString rate); 2. 在发送模块的成员函数中发出信号 emit sendRate(u ...