Golang实现一致性Hash算法

1. 实现原理

实现原理:将key映射到 2^32 的空间中,将这个数字的首尾相连,形成一个环

  • 计算节点(使用节点名称、编号、IP地址)的hash值,放置在环上
  • 计算key的hash值,放置在环上,顺时针寻找到的第一个节点,就是应选取的节点

在这里插入图片描述

例如:p2、p4、p6三个节点,key11、key2、key27按照顺序映射到p2、p4、p6上面,假设新增一个节点p8在p6节点之后,这个时候只需要将key27从p6调整到p8就可以了也就是说,每次新增删除节点时,只需要重新定位该节点附近的一小部分数据

2. 数据倾斜问题

如果服务器的节点过少,容易引起key的倾斜。例如上面的例子中p2、p4、p6分布在环的上半部分,下半部分是空的。那么映射到下半部分的key都会被分配给p2,key过度倾斜到了p2缓存间节点负载不均衡;
为了解决这个问题,引入了虚拟节点的概念,一个真实的节点对应多个虚拟的节点
假设1个真实的节点对应3个虚拟节点,那么p1对应的就是p1-1、p1-2、p1-3

  • 计算虚拟节点的Hash值,放置在环上
  • 计算key的Hash值,在环上顺时针寻找到对应选取的虚拟节点,例如:p2-1,对应真实的节点p2

虚拟节点扩充了节点的数量,解决了节点较少的情况下数据倾斜的问题,而且代价非常小,只需要新增一个字典(Map)维护真实的节点与虚拟节点的映射关系就可以了

3. 代码实现

3.1 consistenthash

//Hash 定义计算hash的函数
type Hash func(data []byte) uint32

type Map struct {

	//自定义的hash函数
	hash Hash

	//虚拟节点数
	replicas int

	//hash环,每个节点映射的hash值
	keys []int

	//映射虚拟节点
	hashMap map[int]string
}

// New 创建一个Map实例,作为一致性hash算法的主要结构
func New(replicas int, fn Hash) *Map {
	//创建实例
	m := &Map{
		replicas: replicas,
		hash:     fn,
		hashMap:  make(map[int]string),
	}

	if m.hash == nil {
		//采用默认的hash算法
		m.hash = crc32.ChecksumIEEE
	}
	return m
}

//Add 添加节点,可以传入一个或者多个真实节点的名称
func (m *Map) Add(keys ...string) {
	for _, key := range keys {
		//根据指定的虚拟节点数量进行创建
		for i := 0; i < m.replicas; i++ {
			//计算hash值,根据 id+key 的格式来进行不同虚拟节点的区分 (strconv.Itoa(i)格式化成string类型)
			hash := int(m.hash([]byte(strconv.Itoa(i) + key)))
			//将对应的hash值,添加进行
			m.keys = append(m.keys, hash)
			//映射虚拟节点和真实节点的关系
			m.hashMap[hash] = key
		}
	}
	sort.Ints(m.keys)
}

//Get 获取到节点信息
func (m *Map) Get(key string) string {
	if len(key) == 0 {
		return ""
	}
	//根据key计算节点的hash
	hash := int(m.hash([]byte(key)))

	//Search方法,根据keys的数量进行遍历
	idx := sort.Search(len(m.keys), func(i int) bool {
		//顺时针寻找第一个匹配的虚拟节点对应的下标,为什么要大于等于呢,因为上面对keys进行了排序,并且顺时针获取到环上面的第一个节点
		return m.keys[i] >= hash
	})
	// 如果 idx == len(m.keys) 说明应该选择 m.keys[0],因为keys是一个环状结构,所以用取余数的方式来处理;
	return m.hashMap[m.keys[idx%len(m.keys)]]
}

3.2 consistenthash_test

func TestHashing(t *testing.T) {
	m := New(3, func(data []byte) uint32 {
		i, _ := strconv.Atoi(string(data))
		return uint32(i)
	})

	m.Add("6", "2", "4")

	//测试通过对应的key对应真实的节点
	testCases := map[string]string{
		"2":  "2",
		"11": "2",
		"23": "4",
		"27": "2",
	}

	for k, v := range testCases {
		if m.Get(k) != v {
			t.Errorf("Asking for %s, shuold have yielded %s", k, v)
		}
	}
	m.Add("8")
	testCases["27"] = "8"

	for k, v := range testCases {
		if m.Get(k) != v {
			t.Errorf("Asking for %s, should have yielded %s", k, v)
		}
	}
}

参考文档:https://geektutu.com/post/geecache-day4.html

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: PBFT(Practical Byzantine Fault Tolerance)共识算法是一种分布式系统中的共识算法,它用于在存在恶意节点的情况下达成一致。在Go语言中,可以使用Go的标准库以及第三方库来实现PBFT共识算法。 具体实现步骤如下: 1. 定义网络中的节点数量以及角色(例如:主节点,从节点)。 2. 实现请求阶段,即请求节点向其他节点发送请求消息。 3. 实现预备阶段,即其他节点对请求消息进行验证,如果足够多的节点同意,则进入下一个阶段。 4. 实现提交阶段,即同意请求的节点向其他节点发送提交消息。 5. 实现确认阶段,即其他节点对提交消息进行验证,如果足够多的节点同意,则完成共识。 实现PBFT共识算法需要对分布式系统和网络通信等方面有较深的了解,如果不熟悉可以先学习相关知识。 ### 回答2: Golang语言是一种由Google开发的开源编程语言,具有高效的并发性能和简单的语法。实现PBFT共识算法使用Golang语言可以提供良好的可读性和易于维护的代码。 PBFT(Practical Byzantine Fault Tolerance)共识算法是一种经典的拜占庭容错共识算法,用于在分布式系统中达成一致的决策。它可以容忍最多 f 个节点(其中 f 是拜占庭错误数),并确保所有正确节点在有限时间内达成一致。 在使用Golang实现PBFT共识算法时,我们可以使用Golang的并发机制,如goroutine和channel,来实现节点之间的消息传递和状态同步。 首先,我们需要定义一个PBFT节点的结构体,包含节点的标识符、状态、消息队列等信息。然后,我们可以使用goroutine创建多个节点,并通过channel进行通信。 在PBFT算法中,节点需要按照一定规则进行投票,并等待足够多的节点达成一致后才能继续执行下一步操作。我们可以使用Golang的select语句和channel来实现这一过程,通过select语句监听不同的channel,根据接收到的消息进行相应的操作。 在实现PBFT算法的消息传递过程中,我们可以使用Golang的网络编程库,如net包,来模拟节点之间的网络通信。节点之间可以通过TCP或UDP协议进行消息的传递和接收。 最后,我们需要编写一些测试用例来验证PBFT共识算法的正确性和性能。使用Golang的测试框架,例如testing包,可以方便地编写单元测试和性能测试。 总之,使用Golang语言实现PBFT共识算法可以充分发挥Golang的并发性能和简洁的语法特性。通过合理地利用Golang的并发机制和网络编程库,我们可以实现一个高效可靠的PBFT共识算法,并进行相关的测试和验证。 ### 回答3: Golang语言可以用于实现PBFT(Practical Byzantine Fault Tolerance)共识算法。PBFT是一种拜占庭容错的共识算法,适用于分布式系统中的节点之间存在可能故障和恶意行为的情况。 Golang语言有以下特点适合实现PBFT共识算法: 1. 并发性能强:Golang通过Goroutine和信道(Channel)实现了高效的并发,能够并行处理多个请求和节点之间的通信。这使得Golang非常适合处理PBFT算法中节点之间的消息传递和状态更新。 2. 跨平台支持广泛:Golang语言具有很好的跨平台支持,可以轻松部署在各种操作系统上。这对于实现一个可靠的共识算法来说非常重要,因为共识算法涉及到多个节点的交互和协同工作。 3. 内置网络库强大:Golang提供了内置的网络库,例如net包和http包,可以方便地实现节点之间的网络通信和消息传递。 4. 代码可读性好:Golang语言具有简洁的语法和良好的代码结构,使得代码易于阅读和理解。这对于共识算法这样复杂且关键的系统来说是至关重要的。 因此,使用Golang语言实现PBFT共识算法是非常可行的。开发者可以利用Golang的并发特性、跨平台支持和丰富的网络库进行快速而可靠的开发,以实现一个高性能和鲁棒的PBFT共识算法
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值