一致性哈希库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

138 lines
2.8 KiB

  1. package hashring
  2. import (
  3. "crypto/sha1"
  4. "sync"
  5. // "hash"
  6. "math"
  7. "sort"
  8. "strconv"
  9. )
  10. const (
  11. //DefaultVirualSpots default virual spots
  12. DefaultVirualSpots = 400
  13. )
  14. type node struct {
  15. nodeKey string
  16. spotValue uint32
  17. }
  18. type nodesArray []node
  19. func (p nodesArray) Len() int { return len(p) }
  20. func (p nodesArray) Less(i, j int) bool { return p[i].spotValue < p[j].spotValue }
  21. func (p nodesArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
  22. func (p nodesArray) Sort() { sort.Sort(p) }
  23. //HashRing store nodes and weigths
  24. type HashRing struct {
  25. virualSpots int
  26. nodes nodesArray
  27. weights map[string]int
  28. mu sync.RWMutex
  29. }
  30. //NewHashRing create a hash ring with virual spots
  31. func NewHashRing(spots int) *HashRing {
  32. if spots == 0 {
  33. spots = DefaultVirualSpots
  34. }
  35. h := &HashRing{
  36. virualSpots: spots,
  37. weights: make(map[string]int),
  38. mu: sync.RWMutex{},
  39. }
  40. return h
  41. }
  42. //AddNodes add nodes to hash ring
  43. func (h *HashRing) AddNodes(nodeWeight map[string]int) {
  44. h.mu.Lock()
  45. defer h.mu.Unlock()
  46. for nodeKey, w := range nodeWeight {
  47. h.weights[nodeKey] = w
  48. }
  49. h.generate()
  50. }
  51. //AddNode add node to hash ring
  52. func (h *HashRing) AddNode(nodeKey string, weight int) {
  53. h.mu.Lock()
  54. defer h.mu.Unlock()
  55. h.weights[nodeKey] = weight
  56. h.generate()
  57. }
  58. //RemoveNode remove node
  59. func (h *HashRing) RemoveNode(nodeKey string) {
  60. h.mu.Lock()
  61. defer h.mu.Unlock()
  62. delete(h.weights, nodeKey)
  63. h.generate()
  64. }
  65. //UpdateNode update node with weight
  66. func (h *HashRing) UpdateNode(nodeKey string, weight int) {
  67. h.mu.Lock()
  68. defer h.mu.Unlock()
  69. h.weights[nodeKey] = weight
  70. h.generate()
  71. }
  72. func (h *HashRing) generate() {
  73. var totalW int
  74. for _, w := range h.weights {
  75. totalW += w
  76. }
  77. totalVirtualSpots := h.virualSpots * len(h.weights)
  78. h.nodes = nodesArray{}
  79. for nodeKey, w := range h.weights {
  80. spots := int(math.Floor(float64(w) / float64(totalW) * float64(totalVirtualSpots)))
  81. for i := 1; i <= spots; i++ {
  82. hash := sha1.New()
  83. hash.Write([]byte(nodeKey + ":" + strconv.Itoa(i)))
  84. hashBytes := hash.Sum(nil)
  85. n := node{
  86. nodeKey: nodeKey,
  87. spotValue: genValue(hashBytes[6:10]),
  88. }
  89. h.nodes = append(h.nodes, n)
  90. hash.Reset()
  91. }
  92. }
  93. h.nodes.Sort()
  94. }
  95. func genValue(bs []byte) uint32 {
  96. if len(bs) < 4 {
  97. return 0
  98. }
  99. v := (uint32(bs[3]) << 24) | (uint32(bs[2]) << 16) | (uint32(bs[1]) << 8) | (uint32(bs[0]))
  100. return v
  101. }
  102. //GetNode get node with key
  103. func (h *HashRing) GetNode(s string) string {
  104. h.mu.RLock()
  105. defer h.mu.RUnlock()
  106. if len(h.nodes) == 0 {
  107. return ""
  108. }
  109. hash := sha1.New()
  110. hash.Write([]byte(s))
  111. hashBytes := hash.Sum(nil)
  112. v := genValue(hashBytes[6:10])
  113. i := sort.Search(len(h.nodes), func(i int) bool { return h.nodes[i].spotValue >= v })
  114. if i == len(h.nodes) {
  115. i = 0
  116. }
  117. return h.nodes[i].nodeKey
  118. }