GroupCache源码分析(3):consistenthash
(1)概述:
一致性哈希原理参考极客兔兔的文章:
动手写分布式缓存 - GeeCache第四天 一致性哈希(hash)
(2)源码:
// Package consistenthash provides an implementation of a ring hash.
// Package consistenthash 提供了一个哈希环
package consistenthash
import (
"hash/crc32"
"sort"
"strconv"
)
// Hash maps bytes to uint32
type Hash func(data []byte) uint32
type Map struct {
hash Hash
// 虚拟节点倍数
replicas int
// 哈希环 keys
keys []int // Sorted
// 虚拟节点与真实节点之间的联系,键是虚拟节点的哈希值,值是真实节点的名称
hashMap map[int]string
}
// New 采取依赖注入的方式,允许用于替换成自定义的 Hash 函数,默认为 crc32.ChecksumIEEE 算法。
func New(replicas int, fn Hash) *Map {
m := &Map{
replicas: replicas,
hash: fn,
hashMap: make(map[int]string),
}
// 如果 fn 为空,也就是不自定义设计哈希值,则默认为 crc32.ChecksumIEEE 算法
if m.hash == nil {
m.hash = crc32.ChecksumIEEE
}
return m
}
// IsEmpty returns true if there are no items available.
func (m *Map) IsEmpty() bool {
return len(m.keys) == 0
}
// Add adds some keys to the hash.
// 添加 keys 到 hash 中
func (m *Map) Add(keys ...string) {
for _, key := range keys {
// 创建虚拟节点和真实节点之间的映射
for i := 0; i < m.replicas; i++ {
// 计算hash值
hash := int(m.hash([]byte(strconv.Itoa(i) + key)))
// 加入到keys
m.keys = append(m.keys, hash)
m.hashMap[hash] = key
}
}
// 给keys排列
sort.Ints(m.keys)
}
// Get gets the closest item in the hash to the provided key.
// Get 拿到最近的一个节点提供给键
func (m *Map) Get(key string) string {
if m.IsEmpty() {
return ""
}
// 计算 key 的哈希值
hash := int(m.hash([]byte(key)))
// Binary search for appropriate replica.
// 二分检索拿到 大于等于 key的哈希值的第一个虚拟节点
idx := sort.Search(len(m.keys), func(i int) bool { return m.keys[i] >= hash })
// Means we have cycled back to the first replica.
// 因为是一个哈希环,所以如果idx等于 keys的长度的话,那它的下标就应该是0
if idx == len(m.keys) {
idx = 0
}
return m.hashMap[m.keys[idx]]
}
一致性哈希就到这。