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] = ¶ms } // 对所有虚拟节点的哈希值进行排序,方便之后进行二分查找 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 }