github.com/weedge/lib@v0.0.0-20230424045628-a36dcc1d90e4/balance/consistenthash.go (about) 1 package balance 2 3 // 一致性HASH负载,采用CRC32算法, 4 // 单调性:环形空间,0 ~ 2^32-1次方的数值空间 5 // 平衡性: 哈希算法并不能保证100%的平衡性,引入虚拟节点分散负载 6 import ( 7 "fmt" 8 "hash/crc32" 9 "sort" 10 "strconv" 11 "sync" 12 ) 13 14 type UInt32Slice []uint32 15 16 func (s UInt32Slice) Len() int { 17 return len(s) 18 } 19 20 func (s UInt32Slice) Less(i, j int) bool { 21 return s[i] < s[j] 22 } 23 24 func (s UInt32Slice) Swap(i, j int) { 25 s[i], s[j] = s[j], s[i] 26 } 27 28 type Hash func(data []byte) uint32 29 30 type HashRing struct { 31 hash Hash 32 replicas int // 复制因子,虚拟节点 33 keys UInt32Slice // 已排序的节点哈希切片 34 Nodes map[uint32]string // 节点哈希和KEY的HashRing,键是哈希值,值是节点Key 35 mutex sync.RWMutex 36 } 37 38 func NewHashRing() *HashRing { 39 hashRing := &HashRing{ 40 replicas: 400, // 复制因子,虚拟节点200个,改动此处会影响已有key的hash的节点 41 hash: crc32.ChecksumIEEE, //使用CRC32算法 42 Nodes: make(map[uint32]string), 43 } 44 return hashRing 45 } 46 47 func (hashRing *HashRing) IsEmpty() bool { 48 return len(hashRing.keys) == 0 49 } 50 51 // Add 方法用来添加缓存节点,参数为节点key,比如使用IP 52 func (hashRing *HashRing) AddNodes(nodes ...string) { 53 hashRing.mutex.Lock() 54 defer hashRing.mutex.Unlock() 55 for _, node := range nodes { 56 // 结合复制因子计算所有虚拟节点的hash值,并存入m.keys中,同时在m.Nodes中保存哈希值和key的映射 57 for i := 0; i < hashRing.replicas; i++ { 58 hash := hashRing.hash([]byte(node + strconv.Itoa(i))) 59 if _, ok := hashRing.Nodes[hash]; !ok { 60 hashRing.keys = append(hashRing.keys, hash) 61 hashRing.Nodes[hash] = node 62 } 63 } 64 } 65 // 对所有虚拟节点的哈希值进行排序,方便之后进行二分查找 66 sort.Sort(hashRing.keys) 67 } 68 69 // Get 方法根据给定的对象获取最靠近它的那个节点key 70 func (hashRing *HashRing) GetNode(key string) string { 71 if hashRing.IsEmpty() { 72 return "" 73 } 74 hashRing.mutex.RLock() 75 defer hashRing.mutex.RUnlock() 76 hash := hashRing.hash([]byte(key)) 77 // 通过二分查找获取最优节点,第一个节点hash值大于对象hash值的就是最优节点 78 idx := sort.Search(len(hashRing.keys), func(i int) bool { return hashRing.keys[i] >= hash }) 79 80 // 如果查找结果大于节点哈希数组的最大索引,表示此时该对象哈希值位于最后一个节点之后,那么放入第一个节点中 81 if idx == len(hashRing.keys) { 82 idx = 0 83 } 84 return hashRing.Nodes[hashRing.keys[idx]] 85 } 86 87 func (hashRing *HashRing) printKeys() { 88 fmt.Println("keys:", hashRing.keys) 89 }