如何保證NFS文件鎖的一致性?

阿里妹導讀:在存儲系統中, NFS(Network File System,即網絡文件系統)是一個重要的概念,已成為兼容POSIX語義的分布式文件系統的基礎。它允許在多個主機之間共享公共文件系統,并提供數據共享的優勢,從而最小化所需的存儲空間。本文將通過分析NFS文件鎖狀態視圖一致性的原理,幫助大家理解NFS的一致性設計思路。
文件鎖
文件鎖是文件系統的最基本特性之一,應用程序借助文件鎖可以控制其他應用對文件的并發訪問。NFS作為類UNIX系統的標準網絡文件系統,在發展過程中逐步地原生地支持了文件鎖(從NFSv4開始)。NFS從上個世界80年代誕生至今,共發布了3個版本:NFSv2、NFSv3、NFSv4。
NFSv4最大的變化是有“狀態”了。某些操作需要服務端維持相關狀態,如文件鎖,例如客戶端申請了文件鎖,服務端就需要維護該文件鎖的狀態,否則和其他客戶端沖突的訪問就無法檢測。如果是NFSv3就需要NLM協助才能實現文件鎖功能,但是有的時候兩者配合不夠協調就會容易出錯。而NFSv4設計成了一種有狀態的協議,自身就可以實現文件鎖功能,也就不需要NLM協議了。
應用接口
應用程序可以通過 fcntl() 或 flock() 系統調用管理NFS文件鎖,下面是NAS使用NFSv4掛載時獲取文件鎖的調用過程:

 從上圖調用棧容易看出,NFS文件鎖實現邏輯基本復用了VFS層設計和數據結構,在通過RPC從Server成功獲取文件鎖后,調用 locks_lock_inode_wait() 函數將獲得的文件鎖交給VFS層管理,關于VFS層文件鎖設計的相關資料比較多,在此就不再贅述了。
EOS原理
文件鎖是典型的非冪等操作,文件鎖操作的重試和Failover會導致文件鎖狀態視圖在客戶端和服務端間的不一致。NFSv4借助SeqId機制設計了最多執行一次的機制,具體方法如下:
針對每個open/lock狀態,Client和Server同時獨立維護seqid,Client在發起會引起狀態變化的操作時(open/close/lock/unlock/release_lockowner)會將seqid加1,并作為參數發送給Server,假定Client發送的seqid為R,Server維護的seqid為L,則:

  • 若R == L +1,表示合法請求,正常處理之。
  • 若R == L,表示重試請求,Server將緩存的reply返回即可。
  • 其他情況均為非法請求,決絕訪問。

根據上述規則,Server可判斷操作是否為正常、重試或非法請求。
該方法能夠保證每個文件鎖操作在服務端最多執行一次,解決了RPC重試帶來的重復執行的問題,但是僅靠這一點是不夠的。比如LOCK操作發送后調用線程被信號中斷,此后服務端又成功接受并執行了該LOCK操作,這樣服務端就記錄了客戶端持有了鎖,但客戶端中卻因為中斷而沒有維護這把鎖,于是就造成了客戶端和服務端間的鎖狀態視圖不一致。因此,客戶端還需要配合處理異常場景,最終才能夠保證文件鎖視圖一致性。
異常處理
由上一節的分析可知,客戶端需要配合處理異常場景才能夠保證文件視圖一致性,那么客戶端設計者主要做了哪些配合的設計呢?目前客戶端主要從SunRPC和NFS協議實現兩個維度相互配合解決該問題,下面分別介紹這兩個維度的設計如何保證文件鎖狀態視圖一致性。
SunRPC設計
SunRPC是Sun公司專門為遠程過程調用設計的網絡通訊協議,這里從保障文件鎖視圖一致性的維度來了解一下SunRPC實現層面的設計理念:
(1)客戶端使用int32_t類型的xid標識上層使用者發起的每個遠程過程調用過程,每個遠程過程調用的多次RPC重試使用相同的xid標識,這樣就保障了多次RPC重試中任何一個返回都可以告知上層遠程過程調用已經成功,保證了服務端執行遠程過程調用執行耗時較長時也能拿到結果,這一點和傳統的netty/mina/brpc等都需要每個RPC都要有獨立的xid/packetid不同。
(2)服務端設計了DRC(duplicate request cache)緩存最近執行的RPC結果,接收到RPC時會首先通過xid檢索DRC緩存,若命中則表明RPC為重試操作,直接返回緩存的結果即可,這在一定程度上規避了RPC重試帶來的重復執行的問題。為了避免xid復用導致DRC緩存返回非預期的結果,開發者通過下述設計進一步有效地減少復用引起錯誤的概率:

  • 客戶端建立新鏈接時初始xid采用隨機值。
  • 服務端DRC會額外記錄請求的校驗信息,緩存命中時會同時校驗這些信息。

(3)客戶端允許在獲得服務端響應前無限重試,保證調用者能夠獲得服務端確定性的執行結果,當然這樣的策略會導致無響應時調用者會一直hang。
(4)NFS允許用戶在掛載時通過soft/hard參數指定SunRPC的重試策略,其中soft模式禁止超時后重試,hard模式則持續重試。當用戶使用soft模式掛載時NFS實現不保證客戶端和服務端狀態視圖的一致性,在遇到遠程過程調用返回超時要求應用程序配合狀態的清理和恢復,比如關閉訪問出錯的文件等,然而實踐中很少有應用程序會配合,所以一般情況下NAS用戶都使用hard模式掛載。
總之,SunRPC要解決的核心問題之一是,遠程過程調用執行時間是不可控的,協議設計者為此定制化設計,盡量避免非冪等操作RPC重試帶來的副作用。
信號中斷
應用程序等待遠程過程調用結果時允許被信號中斷。當發生信號中斷時,由于沒有得到遠程過程調用的執行結果,所以客戶端和服務端的狀態很可能就不一致了,比如加鎖操作在服務端已經成功執行,但客戶端并不知道這個情況。這就要求客戶端做額外的工作將狀態和服務端恢復一致。下面簡要分析獲取文件鎖被信號中斷后的處理,來說明NFS協議實現層面的一致性設計。
通過獲取NFSv4文件鎖的過程可知,NFSv4獲取文件鎖最終會調用 _nfs4_do_setlk() 函數發起RPC操作,最終調用 nfs4_wait_for_completion_rpc_task() 等待,下面是相關代碼:

static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int recovery_type) {        ......       task = rpc_run_task(&task_setup_data);       if (IS_ERR(task))               return PTR_ERR(task);       ret = nfs4_wait_for_completion_rpc_task(task);       if (ret == 0) {               ret = data->rpc_status;               if (ret)                           nfs4_handle_setlk_error(data->server, data->lsp,                                       data->arg.new_lock_owner, ret);           } else                   data->cancelled = 1;         ......     }

通過分析 nfs4_wait_for_completion_rpc_task() 實現可知,當ret < 0時,表明獲取鎖過程被信號中斷,并使用 struct nfs4_lockdata 的 cancelled 成員記錄。繼續查看rpc_task完成后釋放時的回調函數 nfs4_lock_release():

從上面紅色框中的代碼可知,nfs4_lock_release() 檢測到存在信號中斷時會調用 nfs4_do_unlck()函數嘗試將可能成功獲得文件鎖釋放掉,注意此時沒有調用 nfs_free_seqid() 函數將持有的nfs_seqid釋放掉,這是為了:

  • 保證訂正狀態過程中不會有用戶新發起的并發加鎖或者釋放鎖操作,簡化實現。
  • 保證hard模式下UNLOCK操作只會在LOCK操作返回后才會發送,保障已經獲得鎖能夠被釋放掉。

客戶端通過上面的方法能夠有效地保證信號中斷后客戶端和服務端鎖狀態的最終一致性,但也是在損失一部分可用性為代價的。
總結
文件鎖是文件系統原生支持的基礎特性,NAS作為共享的文件系統要面臨客戶端和服務端鎖狀態視圖一致性的問題,NFSv4.0在一定程度上解決了這個問題,當然,技術前進的腳步不會停止,NFS的更新迭代也就不會停止,未來的NFS將會有更多的期待。
最后
我們相信技術的力量,更相信擁有技術力量的人。我們期待存儲的未來,更期待與你一起創造未來。

來源:阿里技術,本文觀點不代表自營銷立場,網址:http://www.wfapiao.com/p/47111

發表評論

登錄后才能評論
侵權聯系
返回頂部
AV天堂日本AV天堂欧美AV天堂