ETCD探索-Lease
2020/2/20 5:01:46
本文主要是介绍ETCD探索-Lease,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
ETCD探索-Lease
梗概
租约,是ETCD的重要特性,用于实现key定时删除功能。与Redis的定时删除功能基本一致。
猜想
我们通常是这么使用Lease的,首先申请一个租约:lease,然后将这个租约赋给一对KeyValue。
ETCD-Lease的实现不难,在讨论怎么实现之前,可以先猜测下。
我的直观想法:
func putWithLease(key string, value string, ttl int) { go func() { time.Sleep(ttl * time.Second) delete(key) }() put(key, value) }
简单说明,当put一对kv时,开启一个协程用于计时。当过了ttl后,将该key删除。
这么做可以实现key的定时删除功能,但有一些问题:
- 不容易续租(续租:延长ttl)
- 不容易提前删除租约
之所以说不容易
,是说你可以通过添加复杂的逻辑实现这些功能,但这样做有一个无法避免的问题:
- 当租约很多时,协程就会很多
虽然起一个协程成本很低,但过多的协程对资源浪费严重,还有可能被操纵系统强行kill。
那么我们来看下ETCD是如何实现Lease的
实现
结构体介绍
- backend
在我们对MVCC的介绍中,我们知道ETCD的数据最终都是存在backend结构体中,所以backend掌握了对数据的增、删、改、查。租约使用了backend的删除能力。
- Lease
租约,包含租约ID、ttl、过期时间等属性。
- LeaseItem
只有一个属性:key。即保存了租约依附的key。说白了就是Key
- LeaseQueue
租约队列,多个租约是以队列的形式保存在LeaseQueue中。
- Lessor
对租约的封装。暴露出一系列操作租约的方法,比如创建、销毁、延长租约的方法。
如何使用租约
我如果想给key=foo绑定一个租约,并且时间过期后将key删除
func testLease() { le := newLessor() // 创建一个lessor le.Promote(0) // 将lessor设置为Primary,这个与raft会出现网络分区有关,不了解可以忽略 go func() { // 开启一个协程,接收过期的key,主动删除 for { expireLease := <-le.ExpiredLeasesC() for _, v := range expireLease { le.Revoke(v.ID) // 通过租约ID删除租约,删除租约时会从backend中删除绑定的key } } }() ttl = 5 // 过期时间设置5s lease := le.Grant(id, ttl) // 申请一个租约 le.Attach(lease, "foo") // 将租约绑定在"foo"上 time.Sleep(10 * time.Second) // 阻塞10s,方便看到结果 }
以上展示了是如何使用lessor这个结构体的。不难看出,lessor提供了Grant、Revoke、Attach等一系列对租约的操作。同时有一点需要注意,lessor不会主动删除过期的租约,而是将过期的lease通过一个chan发送出来,由使用者主动删除。
首先我们看下Grant,申请一个租约的过程
lessor中维护了三个数据结构
- LeaseMap
map[LeaseID]*Lease
用于根据LeaseID快速找到*Lease - ItemMap
map[LeaseItem]LeaseID
用于根据LeaseItem快速找到LeaseID,从而找到*Lease - LeaseExpiredNotifier
LeaseExpiredNotifier是对LeaseQueue的一层封装,他实现了快要到期的租约永远在队头
。
正如图中所述,LeaseQueue是一个优先级队列,每次插入都会根据过期时间插入到合适的位置。通过这个队列,我们只需要不断检查队头的租约是否到期即可,而避免了猜想
中的方法,为每一个租约起一个协程。
关于优先级队列,普遍的做法都是用堆来实现,ETCD中也不例外,他用的是GO标准库中的container/heap
来实现的。这里不具体说了。
从图中可以看出,当Grant一个租约l时,l被同时放到了LeaseMap和LeaseExpiredNotifier中。
在队列头,有一个工作协程revokeExpiredLeases不断的查看队头的租约是否过期,如果过期就放入expiredChan中,不过此时不会pop。(只有revoke才会从队头删除)
再看下Attach的过程
Attach首先用LeaseID去LeaseMap中查询租约是否存在,如果没有这个租约返回错误。
租约存在则首先将Item保存到对应的租约下(图中没有注明),后将Item和LeaseID保存在ItemMap中。
最后看下Revoke过程
通常会有一个协程不断消费expiredChan,将过期的租约Revoke。
Revoke首先根据LeaseID从LeaseMap找到对于的Lease并从LeaseMap中删除,后从Lease中找到绑定的Key,从Backend中将KeyValue删除。
以上便是ETCD-Lease的核心逻辑,与猜想
中的方案对比,我认为最主要的是优先级队列的使用。
Lessor还有一个概念是Primary,只有ETCD集群中的Leader拥有的Lessor是Primary。也只有是Primary的Lessor可以操作租约。因为与Raft相关,而且与Lease的核心逻辑无关,这里不多介绍。
这篇关于ETCD探索-Lease的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-26Rocket消息中间件教程:新手入门详解
- 2024-11-26RocketMQ项目开发教程:新手入门指南
- 2024-11-26MQ源码教程:轻松入门Apache MQ源码解析
- 2024-11-26Rocket消息队列教程:新手入门必读
- 2024-11-26Rocket消息队列教程:新手入门指南
- 2024-11-26RocketMQ底层原理教程:新手入门指南
- 2024-11-26RocketMQ底层原理教程:入门级详解
- 2024-11-26如何获取 OpenAI API Key 用于ChatGPT AI大模型开发?
- 2024-11-26MATLAB 中 A(7)=[];什么意思?-icode9专业技术文章分享
- 2024-11-26UniApp 中如何实现使用输入法时保持页面列表不动的效果?-icode9专业技术文章分享