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))
	}
}

高级功能扩展

我们可以扩展基础实现,增加以下功能:

  1. 权重支持 - 让某些节点处理更多请求
  2. 数据统计 - 查看节点负载情况
  3. 节点健康检查 - 自动剔除不可用节点

带权重和统计的扩展实现

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)
	}
}

性能优化建议

  1. 预分配内存:根据预估节点数预分配 keyshashMap 的大小
  2. 并发安全:添加读写锁支持并发访问
  3. 哈希函数选择:允许自定义更高效的哈希函数
  4. 虚拟节点优化:使用更均匀的虚拟节点命名策略

总结

这个一致性哈希实现提供了以下特性:

  • 可配置的虚拟节点副本数
  • 支持节点权重
  • 简单的负载统计功能
  • 易于扩展的接口设计

你可以根据实际需求进一步扩展,比如添加节点健康检查、数据迁移策略等功能。

回到顶部