consistenthash.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. // Author: simon (ynwdlxm@163.com)
  2. // Date: 2025/10/17 10:50
  3. // Desc: 一致性哈希算法
  4. package consistenthash
  5. import (
  6. "hash/crc32"
  7. "sort"
  8. )
  9. // HashFunc 定义哈希函数类型,接收字节切片并返回32位无符号整数
  10. type HashFunc func(data []byte) uint32
  11. // NodeMap 一致性哈希节点映射结构体
  12. // 用于维护虚拟节点到真实节点的映射关系
  13. type NodeMap struct {
  14. hashFunc HashFunc // 哈希函数
  15. nodeHash []int // 节点哈希值数组
  16. nodeMap map[int]string // 哈希值到节点名称的映射
  17. }
  18. // NewNodeMap 创建新的NodeMap实例
  19. // 如果传入的哈希函数为空,则使用默认的crc32.ChecksumIEEE函数
  20. func NewNodeMap(fn HashFunc) *NodeMap {
  21. m := &NodeMap{
  22. hashFunc: fn,
  23. nodeMap: make(map[int]string),
  24. }
  25. if m.hashFunc == nil {
  26. m.hashFunc = crc32.ChecksumIEEE
  27. }
  28. return m
  29. }
  30. // IsEmpty 判断节点映射是否为空
  31. // 返回true表示没有节点,false表示有节点
  32. func (m *NodeMap) IsEmpty() bool {
  33. return len(m.nodeHash) == 0
  34. }
  35. // AddNode 添加节点到一致性哈希环中
  36. // 支持同时添加多个节点,会为每个节点计算哈希值并维护映射关系
  37. // 空节点会被忽略
  38. func (m *NodeMap) AddNode(keys ...string) {
  39. for _, key := range keys {
  40. if len(key) == 0 {
  41. // key is empty
  42. continue
  43. }
  44. hash := int(m.hashFunc([]byte(key)))
  45. m.nodeHash = append(m.nodeHash, hash)
  46. m.nodeMap[hash] = key
  47. }
  48. // 排序节点哈希值,保证一致性哈希环的有序性
  49. sort.Ints(m.nodeHash)
  50. }
  51. // PickNode 根据键值选择对应的节点
  52. // 通过计算键值的哈希值,在一致性哈希环上找到对应的节点
  53. // 如果哈希环为空则返回空字符串
  54. func (m *NodeMap) PickNode(key string) string {
  55. if m.IsEmpty() {
  56. return ""
  57. }
  58. hash := int(m.hashFunc([]byte(key)))
  59. // 使用二分查找找到第一个大于等于hash值的位置
  60. idx := sort.Search(len(m.nodeHash), func(i int) bool {
  61. return m.nodeHash[i] >= hash
  62. })
  63. // 如果找不到合适的节点,则选择第一个节点(环状结构)
  64. if idx == len(m.nodeHash) {
  65. idx = 0
  66. }
  67. return m.nodeMap[m.nodeHash[idx]]
  68. }