目录
1、原理
原理直接看:5分钟读懂一致性哈希算法原理
2、代码
package main
import (
"fmt"
"hash/crc32"
"log"
"sort"
"strconv"
"strings"
)
//HashFunc 定义生成哈希的函数
type HashFunc func(data []byte) uint32
type Map struct {
hashFunc HashFunc //哈希算法
replicas int //虚拟节点的数量
keys []int //所有虚拟节点的哈希值
hashMap map[int]string //key->虚拟节点的哈希值,value->节点,实现虚拟节点映射到真实节点
}
//New 创建Map
func New(replicas int, fn HashFunc) *Map {
m := &Map{
replicas: replicas,
hashFunc: fn,
hashMap: make(map[int]string),
}
if m.hashFunc == nil {
m.hashFunc = crc32.ChecksumIEEE
}
return m
}
//Map中是否存在节点
func (m *Map) IsEmpty() bool {
return len(m.keys) == 0
}
func (m *Map) AddNode(keys ...string) {
for _, key := range keys {
if key == "" {
continue
}
for i := 0; i < m.replicas; i++ {
//计算虚拟节点哈希值
hash := int(m.hashFunc([]byte(strconv.Itoa(i) + key)))
//存储虚拟节点哈希值
m.keys = append(m.keys, hash)
//存入map做映射
m.hashMap[hash] = key
}
}
//排序哈希值,下面匹配的时候要二分搜索
sort.Ints(m.keys)
}
//支持哈希标记
func getPartitionKey(key string) string {
beg := strings.Index(key, "{")
if beg == -1 {
return key
}
end := strings.Index(key, "}")
if end == -1 || end == beg+1 {
return key
}
return key[beg+1 : end]
}
//获取与key最接近的节点
func (m *Map) PickNode(key string) string {
if m.IsEmpty() {
return ""
}
partitionKey := getPartitionKey(key)
//计算传入key的哈希值
hash := int(m.hashFunc([]byte(partitionKey)))
//sort.Search使用二分查找满足m.Keys[i]>=hash的最小哈希值
idx := sort.Search(len(m.keys), func(i int) bool { return m.keys[i] >= hash })
//若key的hash值大于最后一个虚拟节点的hash值,则选择第一个虚拟节点
if idx == len(m.keys) {
idx = 0
}
return m.hashMap[m.keys[idx]]
}
func TestHash() {
m := New(3, nil)
m.AddNode("a", "b", "c", "d")
if m.PickNode("zxc") != "a" {
log.Println("wrong answer")
}
if m.PickNode("123{abc}") != "b" {
log.Println("wrong answer")
}
if m.PickNode("abc") != "b" {
log.Println("wrong answer")
}
for i := 0; i < 26; i++ {
fmt.Println(string(97+i)+"zhpQQ1159968733", "在[", m.PickNode(string(97+i)), "]节点")
}
}
func main() {
TestHash()
}
执行结果:
[Running] go run "c:\work\1_src\999_test\7_consistent_hashing\main.go"
azhpQQ1159968733 在[ b ]节点
bzhpQQ1159968733 在[ c ]节点
czhpQQ1159968733 在[ b ]节点
dzhpQQ1159968733 在[ a ]节点
ezhpQQ1159968733 在[ b ]节点
fzhpQQ1159968733 在[ c ]节点
gzhpQQ1159968733 在[ b ]节点
hzhpQQ1159968733 在[ a ]节点
izhpQQ1159968733 在[ d ]节点
jzhpQQ1159968733 在[ a ]节点
kzhpQQ1159968733 在[ b ]节点
lzhpQQ1159968733 在[ a ]节点
mzhpQQ1159968733 在[ d ]节点
nzhpQQ1159968733 在[ c ]节点
ozhpQQ1159968733 在[ b ]节点
pzhpQQ1159968733 在[ a ]节点
qzhpQQ1159968733 在[ b ]节点
rzhpQQ1159968733 在[ c ]节点
szhpQQ1159968733 在[ b ]节点
tzhpQQ1159968733 在[ a ]节点
uzhpQQ1159968733 在[ b ]节点
vzhpQQ1159968733 在[ c ]节点
wzhpQQ1159968733 在[ b ]节点
xzhpQQ1159968733 在[ a ]节点
yzhpQQ1159968733 在[ b ]节点
zzhpQQ1159968733 在[ c ]节点
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:
https://course.0voice.com/v1/course/intro?courseId=5&agentId=0