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实现库,适用于分布式系统中的负载均衡场景。

核心特性

  1. 基于Google的跳转一致性哈希算法实现
  2. 支持虚拟节点(vnode)提高分布均匀性
  3. 线程安全,高性能
  4. 支持自定义哈希函数
  5. 简单易用的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)
	}
}

性能优化建议

  1. 虚拟节点数量:默认虚拟节点数量是100,对于节点数量少但需要更均匀分布的场景,可以增加虚拟节点数
h := doublejump.NewHash().SetVnodeCount(200)
  1. 预分配节点:如果知道节点数量,可以预先分配空间
h := doublejump.NewHash().SetExpectedNodeCount(10)
  1. 重用哈希对象:避免频繁创建和销毁哈希对象

注意事项

  1. 节点名称应该是唯一的
  2. 哈希环为空时调用Get会返回空字符串
  3. 删除不存在的节点不会有错误但也不会有任何效果
  4. 权重为0或负数的节点会被忽略

doublejump库通过跳转一致性哈希算法提供了高性能的分布式节点分配方案,特别适合需要动态扩缩容的分布式系统使用。

回到顶部