2023-03-23 15:58:12 +08:00
|
|
|
|
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
|
2023-04-24 15:14:04 +08:00
|
|
|
|
replicas int //复制因子
|
|
|
|
|
keys UInt32Slice //已排序的节点hash切片
|
|
|
|
|
hashMap map[uint32]*model.NodeLastStateItem //节点哈希和key的map, 键是hash值,值是节点key
|
2023-03-23 15:58:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewConsistentHashBalance(replicas int, fn Hash) *ConsistentHashBalance {
|
|
|
|
|
m := &ConsistentHashBalance{
|
|
|
|
|
replicas: replicas,
|
|
|
|
|
hash: fn,
|
2023-04-24 15:14:04 +08:00
|
|
|
|
hashMap: make(map[uint32]*model.NodeLastStateItem),
|
2023-03-23 15:58:12 +08:00
|
|
|
|
}
|
|
|
|
|
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)
|
2023-04-24 15:14:04 +08:00
|
|
|
|
c.hashMap[hash] = ¶ms
|
2023-03-23 15:58:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 对所有虚拟节点的哈希值进行排序,方便之后进行二分查找
|
|
|
|
|
sort.Sort(c.keys)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get 方法根据给定的对象获取最靠近它的那个节点
|
2023-04-24 15:14:04 +08:00
|
|
|
|
func (c *ConsistentHashBalance) Get(key int64) (*model.NodeLastStateItem, error) {
|
2023-03-23 15:58:12 +08:00
|
|
|
|
if c.IsEmpty() {
|
2023-04-24 15:14:04 +08:00
|
|
|
|
return nil, errors.New("node is empty")
|
2023-03-23 15:58:12 +08:00
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
}
|