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  }