简介
NFS4实现“租赁锁”。每个锁拥有一样的“租赁期”。客户端的读写操作将刷新“租赁期”。租赁期到期后,锁将被服务器释放。NFS4通过下述“模型”实现对锁的管理:
1) 清晰地划分客户端和服务器;
2) 可靠的锁的一致性检测机制
3) 简单可靠的锁状态恢复机制
几个概念
Client -- 客户端是访问NFS服务器的资源的实体。客户端是包含直接访问NFS服务器的一个应用程序。客户端可以是传统的操作系统远程文件系统服务的应用程序。客户端负责维护一个或多个用户应用程序的NFS锁。客户端崩溃或失败时,它负责管理重新获取锁。请注意,多个客户端可以共享相同的传输;多个客户端可能存在于同一个网络节点。
Clientid --- 64位的id标识符。客户端经过认证后,由服务器分配的整型数,用于后续通讯中标识客户端。
Server --- 负责协调客户端访问文件系统的实体。
Stateid --- 128位变量用于标识特定文件状态(打开和锁定)。由服务器分配,根据stateid可找到相应的状态信息表。
关键流程
1) 客户端认证(申请clientid)
客户端启动 --> 发送nfs_client_id4(verifier和id串) ,SETCLIENTID
--> 服务器分配clientid --> 客户端发送clientid 给服务器端,二次确认SETCLIENTID_CONFIRM --> 认证成功(建立server记录)
2) 状态建立(申请stateid) --> 带状态的文件操作 --> 撤销状态
客户端用clientid为特定lock_owner发起锁申请 --> 服务器锁定文件,并发回stateid (建立状态表)--> 客户端用stateid进行文件读写--> 撤销状态,即撤销stateid
NFS 4 为支持文件锁需要解决的问题:
1) 如何识别客户端?不同客户端?客户端重启?
2) 如何识别服务器?服务器是否重启?
3) 如何维护锁状态?正常流程?重启恢复?
4) 如何保证“至多一次”的锁状态更新?
5) 状态同步?客户端失败,服务器成功下的状态同步?
1) 如何识别客户端?不同客户端?客户端重启?
每个客户端均必须经过服务器端的认证,并获取服务器端分配的唯一标识符clientid。在后续操作中,NFS系统采用clientid表示该客户端。
客户端发起认证时,需提供两个信息,verifier和id,verifier用于表示该客户端是否是重启客户端(重启客户端将撤销所有此客户端的锁状态);id是该客户端用户标识自己身份的唯一字符串。Id串一般由如下几部分组成:客户端地址(ip+port),服务器地址(ip+port), MAC等机器唯一的信息。socket的通讯五元组可作为同一个server下的唯一标识串,加上MAC等信息是为了防止不同客户端的ip重用。
在上述id生成规则下,不同客户端将生成唯一的标识串。客户端重启时,id不变,仅改变verifier。为了实现重启时id不变,每个客户端配一个监控进程,监控进程生成id和verifier,并启动客户端,重启时改变verifier。
2) 如何识别服务器?服务器是否重启?
服务器的标识放在stateid中,由于服务器的唯一性,可用进程id来标识服务器。服务器重启后,进程id改变,相应的stateid中server标识也将改变,导致stateid失效。同时服务器重启将导致所有锁信息丢失(无信息持久化),也导致stateid失效。
3) 如何维护锁状态?正常流程?重启恢复?
正常流程:
针对特定文件,lock_owner可申请相应的锁(状态申请),得到服务器分配的stateid表示锁请求成功。在后续操作中,该owner均使用stateid进行文件操作。
异常处理流程:
a) 客户端失败:死锁,网络不可达,无法正常工作
NFS 4采用的租赁锁,有特定的租赁期限。若客户端失败,服务器不做任何特殊处理(无法区分客户端正常与否),等待租赁锁到期,锁自然释放。
b) 客户端重启
客户端重启,服务器根据客户端发过来的verifier可知客户端重启,服务器主动释放与该客户端id相关的所有锁。
c) 服务器失败 -- 无法处理,只能重启服务器
d) 服务器重启
服务器重启将导致所有的客户端锁状态失效,客户端进行文件操作时将知道lock状态丢失。当lock状态丢失,client应该重建锁状态。
Server重启后,lease period期间内为客户端重建锁状态时期。在此期间,server可以阻塞所有的读、写、lock等请求,除非server能够确保不发生锁冲突(比如持久化锁状态于磁盘)!
e) 网络不可达
网络不可达,基本等同于客户端失败,服务器等待租赁锁失效。但当网络不可达和服务器重启同时出现时,可能出现两种难以处理的情形,需要持久化存储锁状态方能解决,略去。
4) 如何保证“至多一次”的锁状态更新?
在和文件锁相关的操作中(加锁,升级,降级,解锁),多次操作是不允许的。这就要求相应的操作具有“至多一次(at-most-once)”语义。为实现“至多一次”的锁状态更新,NFS引入“序列化机制”,以应对网络重传和重排序。具体实现如下:每个锁状态更新请求均携带序列号。该序列号是一个连续递增整数,由客户端维护,不同lock_owners拥有不同的序列号,初始值为0。服务器在状态表中缓存最后收到的序列号(last sequence number (L))和应答(response)。只有当下次锁状态更新请求的序列号为L+1时,该请求才被视为有效请求!
注意:
a) 客户端必须保证不多于一个的锁状态更新请求!(同一锁状态更新请求可多次发送,序列号相同)
b) 当服务器收到相同的锁状态更新请求(序列号相同)时,缓存的应答将发送给客户端,而无相应锁操作被执行。
5) 状态同步?客户端失败,服务器成功下的状态同步?
当锁状态更新请求失败后,如超时,服务器的锁状态可能已经改变。为了保证客户端的锁状态的一致性,客户端应该重发“失败”的锁状态更新请求,同步状态,即客户端必须缓存失败的锁状态更新请求,并在下次提交锁状态更新请求前,重发该lock_owner缓存的锁状态更新请求!
“二次确认机制”也能确保状态的一致性,但成本高,重发机制,只有在失败后才会重新发送,成本低(锁状态更新请求远比客户端认证频繁!)。
参考文献: RFC 3530