hpds_control_center/internal/balance/hash.go

91 lines
2.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package balance
import (
"errors"
"fmt"
"hash/crc32"
"hpds_control_center/model"
"sort"
"strconv"
"sync"
)
// Hash 1 单调性(唯一) 2平衡性 (数据 目标元素均衡) 3分散性(散列)
type Hash func(data []byte) uint32
type UInt32Slice []uint32
func (s UInt32Slice) Len() int {
return len(s)
}
func (s UInt32Slice) Less(i, j int) bool {
return s[i] < s[j]
}
func (s UInt32Slice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
type ConsistentHashBalance struct {
mux sync.RWMutex
hash Hash
replicas int //复制因子
keys UInt32Slice //已排序的节点hash切片
hashMap map[uint32]*model.NodeLastStateItem //节点哈希和key的map, 键是hash值值是节点key
}
func NewConsistentHashBalance(replicas int, fn Hash) *ConsistentHashBalance {
m := &ConsistentHashBalance{
replicas: replicas,
hash: fn,
hashMap: make(map[uint32]*model.NodeLastStateItem),
}
if m.hash == nil {
//最多32位保证是一个2^32-1环
m.hash = crc32.ChecksumIEEE
}
return m
}
func (c *ConsistentHashBalance) IsEmpty() bool {
return len(c.keys) == 0
}
// Add 方法用来添加缓存节点参数为节点key比如使用IP
func (c *ConsistentHashBalance) Add(params model.NodeLastStateItem) error {
c.mux.Lock()
defer c.mux.Unlock()
// 结合复制因子计算所有虚拟节点的hash值并存入m.keys中同时在m.hashMap中保存哈希值和key的映射
for i := 0; i < c.replicas; i++ {
hash := c.hash([]byte(strconv.Itoa(i) + fmt.Sprintf("%d", params.NodeId)))
c.keys = append(c.keys, hash)
c.hashMap[hash] = &params
}
// 对所有虚拟节点的哈希值进行排序,方便之后进行二分查找
sort.Sort(c.keys)
return nil
}
// Get 方法根据给定的对象获取最靠近它的那个节点
func (c *ConsistentHashBalance) Get(key int64) (*model.NodeLastStateItem, error) {
if c.IsEmpty() {
return nil, errors.New("node is empty")
}
hash := c.hash([]byte(fmt.Sprintf("%d", key)))
// 通过二分查找获取最优节点,第一个"服务器hash"值大于"数据hash"值的就是最优"服务器节点"
idx := sort.Search(len(c.keys), func(i int) bool { return c.keys[i] >= hash })
// 如果查找结果 大于 服务器节点哈希数组的最大索引,表示此时该对象哈希值位于最后一个节点之后,那么放入第一个节点中
if idx == len(c.keys) {
idx = 0
}
c.mux.RLock()
defer c.mux.RUnlock()
return c.hashMap[c.keys[idx]], nil
}