一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情。
你好,我是 aoho,今天我和你分享的主题是 etcd lease:etcd 如何实现租约?
前面介绍了 etcd watch 实现的机制。本文将会介绍 etcd 的另一个重要特性:lease 租约,类似 TTL(Time To Live),用于 etcd 客户端与服务端之间进行活性检测。在到达 TTL 时间之前,etcd 服务端不会删除相关租约上绑定的键值对;否则超过 TTL 时间则会删除租约上绑定的键值对。因此需要在到达 TTL 时间之前续租,以实现客户端与服务端之间的保活。
lease 也是 etcd v2 与 v3 版本之间的重要变化之一。etcd v2 版本没有 Lease 概念,TTL 直接绑定在 key 上面。每个 TTL、key 创建一个 HTTP/1.x 连接,定时发送续期请求给 etcd server。etcd v3 则在 v2 进行了重大升级,每个 Lease 都设置了一个 TTL 时间,具有相同 TTL 时间的 key 绑定到同一个 Lease,实现了 Lease 的复用,且基于 gRPC 协议的通信实现了连接的多路复用。
本文将会介绍 etcd Lease 的基本用法以及 Lease 实现的原理分析。
如何使用租约
lease 是租约,类似于 Redis 中的 TTL(Time To Live)。可以看一下怎么使用 lease:可以看出来,我们就是拿一个 lease 的 ID 作为凭证。那么,lease 是怎么实现的呢?Lease 在创建的时候,就会分配一个 ID 和设定好 TTL。
在介绍 Lease 的实现原理之前,我们先通过 etcdctl 命令行工具来熟悉 Lease 的用法。依次执行如下的命令:
$ etcdctl lease grant 1000
lease 694d77aa9e38260f granted with TTL(1000s)
$ etcdctl lease timetolive 694d77aa9e38260f
lease 694d77aa9e38260f granted with TTL(1000s), remaining(983s)
$ etcdctl put foo bar --lease 694d77aa9e38260f
OK
# 等待过期
$ etcdctl lease timetolive 694d77aa9e38260f
lease 694d77aa9e38260f already expired
如上的命令中,我们首先创建了一个 Lease,TTL 时间为 1000s;接着根据获取到的 leaseId 查看其存活时间;然后写入一个键值对,并通过 --lease 绑定 lease;最后一条命令是在 1000s 之后再次查看该 lease 对应的存活信息。
通过 etcdctl 命令行工具的形式,我们创建指定 TTL 时间的 Lease,了解了 lease 的基本使用。下面将会具体介绍 lease 的实现。
客户端 API 调用
我们基于 etcd Lease 实现键值对 key=foo 绑定到租约上,到达 TTL 时间后主动将对应的键值对删除,代码如下:
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 通过一个 channel 发送出来,由使用者主动删除。
阅读最新文章,关注公众号:aoho求索