Lease 中文叫租约,是一种广泛应用于分布式系统领域的协议,它是一种维护分布式系统一致性的有效工具。
Lease 通常定义为:颁发者在一定期限内給予持有者一定权利的协议。
Lease 表达了颁发者在一定期限内的承诺,只要未过期颁发者必须严格遵守 lease 约定的承诺。
Lease 的持有者在期限内使用颁发者的承诺,但 lease 一旦过期必须放弃使用或者重新和颁发者续约。
下面看看 lease 机制的几个具体应用。
1. 动态密钥管理
中心密钥服务器维护着全局的密钥生成和发放,所有需要使用密钥的外围系统向密钥服务器申请密钥用于本系统的加解密工作。出于性能和可用性考虑,不能每个请求都向中心服务器去申请,因此密钥通常被缓存在本地系统中。那么当需要修改中心系统的密钥时(出于安全性考虑的定期修改),如何保证所有使用该密钥的本地系统都立刻丢弃过期的密钥,而立刻向中心密钥服务器重新申请最新的密钥,并保持所有系统中密钥的一致性?(不一致可能导致系统不可用)。
这种场景非常适合使用 lease 机制来解决,中心服务器发放密钥的时候,同时发放一个 lease 承诺在一定时间内不修改该密钥。本地系统获取密钥时,同时根据 lease 的约定只在其有效期内使用密钥,lease 一旦过期立刻重新申请密钥。当变更密钥时,在所有已颁发的 lease 全部过期前修改不能生效,并且在变更密钥生效期间不能颁发新的 lease,避免形成活锁(永远等不到所有 lease 失效)。
这个机制大体如上述,但还有一些细节点需要考虑,lease 机制依赖于分布式环境下的服务器时钟同步,如果出现时钟不同步的情况,在这个应用场景下会带来什么影响?如何规避或解决?
中心服务器时钟比客户端系统快:这种情况下,中心服务器将 lease 过期时,客户端服务器还在使用。在这个时间差范围内如果中心服务器变更了密钥,会导致客户端服务器的密钥错误造成服务不可用。这种情况可以设置 lease 颁发者(中心服务器)的有效期设置的比接收者(客户端)更大,大过时钟误差。
中心服务器时钟比客户端系统慢:这种情况下,客户端将 lease 过期时中心服务器还未过期,客户端只需重新发起新的 lease 申请即可,如果此时遇到中心服务器正在进行密钥更新锁定不能颁发 lease,则可只返回当前的密钥数据,而不颁发 lease。客户端将在这个时间窗口中退化为每请求一次性的使用该密钥数据。
客户端接入时的校时验证:考虑到 lease 依赖时钟的精确同步(误差最好不要太大),那么可以考虑客户端向中心请求时携带自身的时间戳,以便中心服务器可以对其进行校时误差,当误差超过允许范围(例如:5秒)时,可以考虑拒绝接入或报警通知。
设计合理的 lease 有效期:考虑在密钥系统这个业务场景下,虽然变更发生的情况比较少。但如果出现安全事件需要紧急变更时需要 lease 快速失效时,最及时的办法是设计客户端的回调接口由中心去通知客户端立刻放弃 lease,这样会增加实现的复杂度。另一种折中的办法是采用较短的失效时间(例如:2分钟),这样可以保证密钥变更会在最长不超过 2分钟 + 时钟误差的范围内完成全局的更新,具体策略的采用可以根据实际需求去权衡。
2. 分布式文件系统
以GFS为例,每个文件块都有多个副本分布在多个chunckserver上,在 并行追加时必须有一个全局统一的追加顺序。
当然这个顺序全部由中心 master 来决定,那 master 将承担过大的负荷。
GFS采用了 lease 机制,就是对每个文件块 master 向 chunkserver 颁发 lease,在 lease 有效期内由它决定并行文件追加的顺序。在 lease 有效期内,chunkserver 可以一直续约,如果出现机器宕机或网络断链时,master 可在 lease 过期后重新选择另一个 chunkserver,只要保证对同一个文件块的并行追加在集群中只有一个 chunkserver 决定其追加顺序即可。
3. 状态检测
在通常的集群系统中,我们采用心跳来检测节点状态。但普通的心跳机制是无协议和承诺约定的,所以它的检测结果可能不可靠。很多监控系统采用心跳检测集群中节点的存活性,这种机制存在误报警的可能。
普通心跳通常是在规定的时限内定期向检测节点发送存活性报告,若超出一段时间未能收到心跳报告,那么检测节点则判断节点可能失效,并采取一系列措施(报警、通知节点的使用者)。这种机制存在的问题是,检测节点单方面判定节点失效,在某些业务集群系统中可能存在风险。节点自身并未认识自己已被认定失效,还在继续提供正常的服务。若该节点在集群中承担唯一 primary 节点的职责,而检测节点的失效判定发起了重新选择新的主节点,会引发“双主”问题。
采用 lease 机制的心跳实现,则彻底避免了此类问题。由于网络分割的原因,其实没有任何技术可以可靠的判定节点状态,但采用 lease 机制的状态检测,可以避免出现误判时引入新的问题。
从以上几个简单的 lease 机制应用,我们可以看出 lease 作为一种协议承诺,其承诺的范围十分宽泛。
如果协议内容是确认客户端存活,那么这个租约就相当于心跳。
如果协议内容是保证内容不会被修改,那么这个租约就相当于读锁。
如果协议内容是保证只能被某个客户端修改,那么这个租约就相当于写锁。