golang实现可配置副本一致性哈希算法插件库consistenthash的使用
Golang实现可配置副本一致性哈希算法插件库consistenthash的使用
简介
consistenthash是一个Go语言实现的零依赖一致性哈希库,基于golang/groupcache进行了一些性能改进:
- 添加了
Remove
函数 - 从哈希环中排序和移除节点(及其副本) - 将int哈希替换为uint32
- 添加新节点时可配置副本数量(当节点容量不同时很有用)
技术细节
该库使用以下数据结构:
- HashMap:将键的哈希映射到原始值,如
0xFF => "node number one"
- BlockMap:具有动态数量的块,每个块包含排序的项列表,每个项有Key和指向HashMap的指针
- ReplicaMap:仅当键的副本数与默认副本数不同时,保存该键的副本数
使用方法
安装:
go get github.com/mbrostami/consistenthash/v2
简单用例
package main
import (
"fmt"
"github.com/mbrostami/consistenthash/v2"
)
func main() {
// 创建一致性哈希实例,设置默认副本数为10
ch := consistenthash.New(consistenthash.WithDefaultReplicas(10))
// 添加节点
ch.Add([]byte("templateA"), []byte("templateB"))
// 获取键"userA"对应的节点
assignedTemplate := ch.Get([]byte("userA")) // 对于"userA",分配的模板总是相同的
fmt.Printf("assigned: %s", assignedTemplate)
}
加权负载
package main
import (
"fmt"
"github.com/mbrostami/consistenthash/v2"
)
func main() {
ch := consistenthash.New(consistenthash.WithDefaultReplicas(10))
// 添加两个普通节点
ch.Add([]byte("127.0.0.1:1001"), []byte("127.0.0.1:1002"))
// 添加一个权重更高的节点(4倍权重)
ch.AddReplicas(40, []byte("127.0.0.1:1003"))
rKey := "something like request url or user id"
// 查找最接近的节点
node := ch.Get([]byte(rKey))
fmt.Println(node) // 输出其中一个节点
}
哈希环示例
package main
import (
"fmt"
"github.com/mbrostami/consistenthash/v2"
)
func main() {
// 创建哈希环,每个节点3个副本
ch := consistenthash.New(consistenthash.WithDefaultReplicas(3))
// 添加3个节点
ch.Add([]byte("A"), []byte("B"), []byte("C"))
// 测试不同键的分布
fmt.Println(ch.Get([]byte("Alica")))
fmt.Println(ch.Get([]byte("Bob")))
fmt.Println(ch.Get([]byte("Casey")))
}
哈希环结构类似下图:
更多关于golang实现可配置副本一致性哈希算法插件库consistenthash的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang实现可配置副本一致性哈希算法插件库consistenthash的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 可配置副本一致性哈希算法插件库 Consistenthash 使用指南
一致性哈希算法是分布式系统中常用的数据分片技术,它能在节点增减时最小化数据迁移量。下面我将介绍如何使用 Golang 实现一个可配置副本数的一致性哈希算法库。
基本实现
首先我们实现一个基础版本的可配置副本一致性哈希:
package consistenthash
import (
"hash/crc32"
"sort"
"strconv"
)
type Hash func(data []byte) uint32
type ConsistentHash struct {
hash Hash // 哈希函数
replicas int // 每个节点的虚拟节点副本数
keys []int // 排序后的哈希环
hashMap map[int]string // 虚拟节点到真实节点的映射
}
func New(replicas int, fn Hash) *ConsistentHash {
if fn == nil {
fn = crc32.ChecksumIEEE
}
return &ConsistentHash{
hash: fn,
replicas: replicas,
hashMap: make(map[int]string),
}
}
// 添加节点到哈希环
func (ch *ConsistentHash) Add(nodes ...string) {
for _, node := range nodes {
for i := 0; i < ch.replicas; i++ {
// 为每个节点创建replicas个虚拟节点
hash := int(ch.hash([]byte(strconv.Itoa(i) + node)))
ch.keys = append(ch.keys, hash)
ch.hashMap[hash] = node
}
}
sort.Ints(ch.keys)
}
// 获取key对应的节点
func (ch *ConsistentHash) Get(key string) string {
if len(ch.keys) == 0 {
return ""
}
hash := int(ch.hash([]byte(key)))
// 二分查找第一个大于等于hash的节点
idx := sort.Search(len(ch.keys), func(i int) bool {
return ch.keys[i] >= hash
})
// 环形处理
if idx == len(ch.keys) {
idx = 0
}
return ch.hashMap[ch.keys[idx]]
}
使用示例
package main
import (
"fmt"
"consistenthash"
)
func main() {
// 创建一致性哈希实例,设置副本数为3
ch := consistenthash.New(3, nil)
// 添加节点
ch.Add("Node1", "Node2", "Node3", "Node4")
// 测试键值分布
keys := []string{"Key1", "Key2", "Key3", "Key4", "Key5"}
for _, key := range keys {
fmt.Printf("Key %s is assigned to %s\n", key, ch.Get(key))
}
// 添加新节点
fmt.Println("\nAdding Node5...")
ch.Add("Node5")
// 再次测试分布
for _, key := range keys {
fmt.Printf("Key %s is now assigned to %s\n", key, ch.Get(key))
}
}
高级功能扩展
我们可以扩展基础实现,增加以下功能:
- 权重支持 - 让某些节点处理更多请求
- 数据统计 - 查看节点负载情况
- 节点健康检查 - 自动剔除不可用节点
带权重和统计的扩展实现
type WeightedNode struct {
Name string
Weight int
}
type Stats struct {
TotalKeys int
NodeStats map[string]int
}
type ConsistentHashWithStats struct {
*ConsistentHash
nodeWeights map[string]int
stats Stats
}
func NewWithStats(replicas int, fn Hash) *ConsistentHashWithStats {
return &ConsistentHashWithStats{
ConsistentHash: New(replicas, fn),
nodeWeights: make(map[string]int),
stats: Stats{
NodeStats: make(map[string]int),
},
}
}
func (c *ConsistentHashWithStats) AddWeighted(nodes ...WeightedNode) {
for _, node := range nodes {
replicas := c.replicas * node.Weight
c.nodeWeights[node.Name] = node.Weight
for i := 0; i < replicas; i++ {
hash := int(c.hash([]byte(strconv.Itoa(i) + node.Name)))
c.keys = append(c.keys, hash)
c.hashMap[hash] = node.Name
}
}
sort.Ints(c.keys)
}
func (c *ConsistentHashWithStats) GetWithStats(key string) (string, *Stats) {
node := c.Get(key)
c.stats.TotalKeys++
c.stats.NodeStats[node]++
return node, &c.stats
}
使用示例
func main() {
// 创建带统计功能的一致性哈希
ch := NewWithStats(3, nil)
// 添加带权重的节点
ch.AddWeighted(
WeightedNode{Name: "Node1", Weight: 2}, // 2倍权重
WeightedNode{Name: "Node2", Weight: 1},
WeightedNode{Name: "Node3", Weight: 1},
)
keys := []string{"Key1", "Key2", "Key3", "Key4", "Key5", "Key6"}
for _, key := range keys {
node, stats := ch.GetWithStats(key)
fmt.Printf("Key %s -> %s\n", key, node)
fmt.Printf("Current stats: %+v\n", stats)
}
}
性能优化建议
- 预分配内存:根据预估节点数预分配
keys
和hashMap
的大小 - 并发安全:添加读写锁支持并发访问
- 哈希函数选择:允许自定义更高效的哈希函数
- 虚拟节点优化:使用更均匀的虚拟节点命名策略
总结
这个一致性哈希实现提供了以下特性:
- 可配置的虚拟节点副本数
- 支持节点权重
- 简单的负载统计功能
- 易于扩展的接口设计
你可以根据实际需求进一步扩展,比如添加节点健康检查、数据迁移策略等功能。