golang实现高性能跳转一致性哈希算法插件库doublejump的使用
Golang实现高性能跳转一致性哈希算法插件库doublejump的使用
概述
Doublejump是对Google跳转一致性哈希算法的改进版本。它克服了原始设计无法删除节点的缺点。
性能基准
Doublejump/10-nodes 49276861 22.3 ns/op 0 B/op 0 allocs/op
Doublejump/100-nodes 33304191 34.9 ns/op 0 B/op 0 allocs/op
Doublejump/1000-nodes 25261296 46.3 ns/op 0 B/op 0 allocs/op
StathatConsistent/10-nodes 4780832 273.5 ns/op 80 B/op 2 allocs/op
StathatConsistent/100-nodes 4059537 291.8 ns/op 80 B/op 2 allocs/op
StathatConsistent/1000-nodes 3132294 367.6 ns/op 80 B/op 2 allocs/op
SerialxHashring/10-nodes 2766384 455.7 ns/op 152 B/op 5 allocs/op
SerialxHashring/100-nodes 2500936 487.6 ns/op 152 B/op 5 allocs/op
SerialxHashring/1000-nodes 2254138 560.0 ns/op 152 B/op 5 allocs/op
安装
V1版本 (Go <= 1.17)
go get -u github.com/edwingeng/doublejump
V2版本 (Go >= 1.18)
go get -u github.com/edwingeng/doublejump/v2
使用示例
V1版本示例 (Go <= 1.17)
import "github.com/edwingeng/doublejump"
func Example() {
// 创建新的哈希环
h := NewHash()
// 添加10个节点
for i := 0; i < 10; i++ {
h.Add(fmt.Sprintf("node%d", i))
}
// 获取节点数量
fmt.Println(h.Len()) // 当前有效节点数
fmt.Println(h.LooseLen()) // 总节点数(包括已删除的)
// 根据key获取节点
fmt.Println(h.Get(1000)) // 获取key=1000对应的节点
fmt.Println(h.Get(2000)) // 获取key=2000对应的节点
fmt.Println(h.Get(3000)) // 获取key=3000对应的节点
// 移除一个节点
h.Remove("node3")
// 再次获取节点数量
fmt.Println(h.Len()) // 当前有效节点数
fmt.Println(h.LooseLen()) // 总节点数(包括已删除的)
// 再次根据key获取节点
fmt.Println(h.Get(1000)) // 获取key=1000对应的节点
fmt.Println(h.Get(2000)) // 获取key=2000对应的节点
fmt.Println(h.Get(3000)) // 获取key=3000对应的节点
// 输出:
// 10
// 10
// node9
// node2
// node3
// 9
// 10
// node9
// node2
// node0
}
V2版本示例 (Go >= 1.18)
import "github.com/edwingeng/doublejump/v2"
func Example() {
// 创建新的哈希环(泛型版本)
h := NewHash[string]()
// 添加10个节点
for i := 0; i < 10; i++ {
h.Add(fmt.Sprintf("node%d", i))
}
// 获取节点数量
fmt.Println(h.Len()) // 当前有效节点数
fmt.Println(h.LooseLen()) // 总节点数(包括已删除的)
// 根据key获取节点(返回节点和是否找到的标志)
fmt.Println(h.Get(1000)) // 获取key=1000对应的节点
fmt.Println(h.Get(2000)) // 获取key=2000对应的节点
fmt.Println(h.Get(3000)) // 获取key=3000对应的节点
// 移除一个节点
h.Remove("node3")
// 再次获取节点数量
fmt.Println(h.Len()) // 当前有效节点数
fmt.Println(h.LooseLen()) // 总节点数(包括已删除的)
// 再次根据key获取节点
fmt.Println(h.Get(1000)) // 获取key=1000对应的节点
fmt.Println(h.Get(2000)) // 获取key=2000对应的节点
fmt.Println(h.Get(3000)) // 获取key=3000对应的节点
// 输出:
// 10
// 10
// node9 true
// node2 true
// node3 true
// 9
// 10
// node9 true
// node2 true
// node0 true
}
致谢
原始算法的实现归功于dgryski。
更多关于golang实现高性能跳转一致性哈希算法插件库doublejump的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang实现高性能跳转一致性哈希算法插件库doublejump的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang高性能跳转一致性哈希算法库doublejump使用指南
doublejump是一个高性能的跳转一致性哈希(consistent hashing with bounded loads)算法的Golang实现库,适用于分布式系统中的负载均衡场景。
核心特性
- 基于Google的跳转一致性哈希算法实现
- 支持虚拟节点(vnode)提高分布均匀性
- 线程安全,高性能
- 支持自定义哈希函数
- 简单易用的API
安装
go get github.com/edwingeng/doublejump
基本使用
1. 创建哈希环
package main
import (
"fmt"
"github.com/edwingeng/doublejump"
)
func main() {
// 创建哈希环
h := doublejump.NewHash()
// 添加节点
h.Add("node1", "node2", "node3")
// 查找key对应的节点
key := "some_key"
node := h.Get(key)
fmt.Printf("Key '%s' is assigned to node: %s\n", key, node)
}
2. 带权重的节点添加
func main() {
h := doublejump.NewHash()
// 添加带权重的节点(权重越高分配的虚拟节点越多)
h.AddWeighted("node1", 3) // 权重3
h.AddWeighted("node2", 2) // 权重2
h.AddWeighted("node3", 1) // 权重1
// 统计分布情况
keyCount := 10000
distribution := make(map[string]int)
for i := 0; i < keyCount; i++ {
key := fmt.Sprintf("key%d", i)
node := h.Get(key)
distribution[node]++
}
for node, count := range distribution {
fmt.Printf("Node %s got %d keys (%.1f%%)\n",
node, count, float64(count)/float64(keyCount)*100)
}
}
3. 删除节点
func main() {
h := doublejump.NewHash()
h.Add("node1", "node2", "node3", "node4")
// 删除节点
h.Remove("node3")
// 验证节点是否已删除
fmt.Println("Contains node3:", h.Contains("node3")) // false
}
高级用法
1. 自定义哈希函数
func main() {
h := doublejump.NewHash()
// 设置自定义哈希函数
h.SetHashFunc(func(key []byte) uint64 {
// 实现你自己的哈希逻辑
return customHash(key)
})
// 添加节点和使用方式不变
h.Add("node1", "node2")
fmt.Println(h.Get("some_key"))
}
2. 批量获取节点
func main() {
h := doublejump.NewHash()
h.Add("node1", "node2", "node3", "node4", "node5")
key := "some_key"
// 获取key对应的前3个候选节点
nodes := h.GetN(key, 3)
fmt.Println("Top 3 nodes for key:", nodes)
}
3. 一致性哈希在负载均衡中的应用
type Server struct {
Addr string
// 其他服务器属性...
}
func main() {
h := doublejump.NewHash()
servers := []*Server{
{Addr: "192.168.1.1:8080"},
{Addr: "192.168.1.2:8080"},
{Addr: "192.168.1.3:8080"},
}
// 添加服务器节点
for _, s := range servers {
h.Add(s.Addr)
}
// 模拟请求路由
requestKeys := []string{"user123", "order456", "product789"}
for _, key := range requestKeys {
serverAddr := h.Get(key)
fmt.Printf("Request %s routed to server %s\n", key, serverAddr)
}
// 当服务器下线时
h.Remove("192.168.1.2:8080")
fmt.Println("\nAfter removing 192.168.1.2:8080:")
for _, key := range requestKeys {
serverAddr := h.Get(key)
fmt.Printf("Request %s now routed to server %s\n", key, serverAddr)
}
}
性能优化建议
- 虚拟节点数量:默认虚拟节点数量是100,对于节点数量少但需要更均匀分布的场景,可以增加虚拟节点数
h := doublejump.NewHash().SetVnodeCount(200)
- 预分配节点:如果知道节点数量,可以预先分配空间
h := doublejump.NewHash().SetExpectedNodeCount(10)
- 重用哈希对象:避免频繁创建和销毁哈希对象
注意事项
- 节点名称应该是唯一的
- 哈希环为空时调用Get会返回空字符串
- 删除不存在的节点不会有错误但也不会有任何效果
- 权重为0或负数的节点会被忽略
doublejump库通过跳转一致性哈希算法提供了高性能的分布式节点分配方案,特别适合需要动态扩缩容的分布式系统使用。