一、概述

HDFS 文件是 write-once-read-many,并且不支持客户端的并行写操作,需要一种机制保证对 HDFS 文件的互斥操作。HDFS 提供了租约(Lease) 机制来实现这个功能,租约是 Namenode 给予租约持有者(LeaseHolder,一般是客户端)在规定时间内拥有文件权限(写文件)的合同。

在 HDFS 中,客户端写文件时需要先从租约管理器(LeaseManager)申请一个租约,成功申请租约之后客户端就成为了租约持有者,也就拥有了对该 HDFS 文件的独占权限,其他客户端在该租约有效时无法打开这个 HDFS 文件进行操作。Namenode 的租约管理器保存了 HDFS 文件与租约、租约与租约持有者的对应关系,租约管理器还会定期检查它维护的所有租约是否过期。租约管理器会强制收回过期的租约,所以租约持有者需要定期更新租约(renew),维护对该文件的独占锁定。当客户端完成了对文件的写操作,关闭文件时,必须在租约管理器中释放租约。

二、理论笔记

三、实现

3.1. 相关类

HDFS 客户端是可以同时打开多个 HDFS 文件进行读写操作的,为了便于管理,在租约管理器中将一个客户端打开的所有文件组织在一起构成一条记录,也就是 LeaseManager.Lease 类。

3.1.1. LeaseManager

LeaseManager 是 Namenode 中维护所有租约操作的类,它不仅仅保存了 HDFS 中所有租约的信息,提供租约的增、删、改、查方法,同时还维护了一个 Monitor 线程定期检查租约是否超时,对于长时间没有更新租约的文件(超过硬限制时间),LeaseManager 会触发租约恢复机制,然后关闭文件。

LeaseManager

3.2. 租约管理

3.2.1. 添加

当客户端创建文件和追加写文件时会调用 LeaseManager.addLease() 为该客户端在 HDFS 文件上添加一个租约。

addLease() 方法有两个参数,其中 holder 参数保存租约的持有者信息,src 参数则保存创建或者追加写文件的路径,这两个参数分别对应于 ClientProtocol.create() 或者 append() 方法中的 clientName 和 src 参数。

addLease() 方法的实现也非常简单,先是通过 getLease()方法构造租约,然后在 LeaseManager 定义的保存租约的数据结构中添加这个租约的信息。

3.2.2. 更新

当客户端打开了一个文件用于写或者追加写操作时,LeaseManager 会保存这个客户端在该文件上的租约。客户端会启动一个 LeaseRenewer 定期更新租约,以防止租约过期。租约更新操作是由 FSNamesystem.renewLease() 响应的,这个方法最终会调用 LeaseManager.renewLease()方法。renewLease()方法会首先从 sortedLeases 字段中移除这个租约,然后更新这个租约的最后更新时间,再重新加入 sortedLeases 中。

renewLease

sortedLeases 是一个以最后更新时间排序的集合,所以每次更新租约后,sortedLeases 中的顺序也需要重新改变。

3.2.3. 删除

从 LeaseManager 保存租约的数据结构中删除租约信息

3.3. 租约检查

租约管理器除了对租约提供增、删、改、查等操作外,还会定期检查所有租约,对于长时间没有进行租约更新的文件,LeaseManager 会对这个文件进行租约恢复操作,然后关闭这个文件。

HDFS 是一个分布式系统,客户端可能在打开一个文件之后出现故障,这也就造成了客户端不能完成租约更新以及写文件之后的租约删除操作,这时就会造成租约过期。

租约的定期检查操作是由 LeaseManager 的内部类 Monitor 执行的,Monitor 是一个线程类,它的 run() 方法会每隔 2 秒调用一次LeaserManager.checkLeases()方法检查租约。

LeaseManager 中有两个限制时间,其中软限制时间用于记录写文件规定的租约超时时间;硬限制时间则用于判断文件是否由于异常而末能正确关
闭。在 checkLeases() 方法中就是使用硬限制时间(60分钟)判断是否需要进行租约恢复操作。

checkLeases() 方法会遍历 leaseManager 中管理的所有租约,找出所有超过硬限制时间而未更新的租约。由于租约保存了这个客户端打开的所有 HDFS 文件,所以 checkLeases() 方法会遍历这个租约上的所有文件,并调用 FSNamesystem.internalReleaseLease 方法进行租约恢复操作。

3.4. 租约恢复

对于 HDFS 文件的租约恢复操作是通过调用 FSNamesystem.interalReleaseLease() 实现

3.4.1. Monitor 线程发起

3.4.2. 其他方式发起

租约恢复除了可以由 LeaseManager.Monitor 线程发起外,还有以下三种情况会调用 FSNamesystem.recoverLeaseInternal() 方法触发租约恢复操作。