The Chubby lock service for loosely-coupled distributed systems 閱讀筆記

聲明

本文是對文獻[1]的閱讀報告,內容非本人原創。

摘要

本文描述了使用Chubby鎖服務的經驗,該服務旨在爲鬆散耦合的分佈式系統提供粗粒度的鎖以及可靠的(儘管容量較小的)存儲。Chubby提供了一個類似於帶有建議鎖的分佈式文件系統的接口,但是設計的重點是可用性和可靠性,而不是高性能。

引言

Chubby的鎖服務,它適用於由高速網絡連接的中等數量的小型機器組成的鬆散耦合分佈式系統。鎖服務的目的是允許其客戶端同步其活動並就其環境的基本信息達成一致。主要目標包括可靠性、對中等規模客戶的可用性和易於理解的語義;吞吐量和存儲容量被認爲是次要的。Chubby的客戶端界面類似於執行整個文件讀寫的簡單文件系統,並增加了建議鎖和各種事件(如文件修改)的通知。Chubby能夠幫助開發人員處理系統內的粗粒度同步,特別是處理從一組對等的服務器中選擇領導者的問題。

合理性

鎖服務有幾個優勢:

  1. 開發者在最開始的時候並不會考慮高可用,加上使用鎖服務就可以讓維護現有的程序結構以及交互模式變得更加簡單。

  2. 我們的許多服務會選擇一個主要的服務,或者在他們的組件之間對數據做區分,這些都需要一個能夠傳播結果的機制。

  3. 程序員更熟悉基於鎖的接口。

  4. 分佈式一致性算法使用多數表決的方法來做決策,所以它們使用副本集來達到高可用。例如,Chubby最少需要3個才能保證正常工作,通常使用5個副本。作爲對比,如果一個客戶端系統使用鎖服務,即便是單獨的客戶端也能獲得鎖來保證程序的安全性。所以,一個鎖服務能夠減少客戶端所依賴的服務數。

一個可能會讓一些讀者感到驚訝的選擇是,我們不期望鎖使用是細粒度的,在這種情況下,它們可能只被持有很短的時間(秒或更短);相反,我們期望粗粒度的使用。例如,應用程序可能使用鎖來選擇主節點,然後主節點將在相當長的一段時間內(可能是幾個小時或幾天)處理對該數據的所有訪問。這兩種使用方式與鎖服務器的要求不同。

粒度鎖對鎖服務器的負載要小得多。特別是,鎖定獲取速率通常與客戶端應用程序的事務速率關係不大。粗粒度鎖很少被獲取,因此臨時鎖服務器不可用對客戶端的延遲更少。另一方面,從客戶機到客戶機的鎖傳輸可能需要昂貴的恢復過程,因此人們不會希望鎖服務器的故障轉移導致鎖丟失。因此,粗粒度鎖在鎖服務器故障中存活是很好的,不需要擔心這樣做的開銷,這樣的鎖允許少量可用性較低的鎖服務器爲許多客戶端提供充分的服務。細粒度鎖會導致不同的結論。即使是短暫的鎖服務器不可用也可能導致許多客戶端停滯。

chubby只提供粗粒度的鎖,但是應用程序可以根據需要實現細粒度的控制。應用程序可以將它的鎖劃分成組,然後使用chubby的粗粒度鎖來分配這些鎖組給特定於應用程序的鎖服務器。小狀態需要維護這些細粒度鎖;這些服務器只需要保持一個非易失性的、單調遞增的獲取計數器,很少更新。客戶端可以在解鎖時得知丟失的鎖,如果使用簡單的固定長度租約,協議可以簡單和高效。

系統結構

Chubby主要有兩個通過RPC進行通信的組件:一個是服務器,另一個是客戶端程序需要鏈接的庫。第三個組件,代理服務器,是可選的。

image_109

由上圖1所示,每個Chubby Cell由一組被稱爲副本的服務器組成,放置這些服務器的目的是減少相關故障的可能性。副本使用分佈式共識協議來選擇主節點;主服務器必須獲得大多數副本的投票,並承諾這些副本在幾秒鐘的時間間隔內不會選舉另一個主服務器,即主租約。主租約由副本定期更新,只要主租約繼續贏得多數選票。
副本維護簡單數據庫的副本,但只有主數據庫啓動對該數據庫的讀寫。所有其他副本只是簡單地從使用共識協議發送的主服務器複製更新。

客戶端通過向DNS中列出的副本發送主位置請求來找到主服務器。非主副本通過返回主副本的標識來響應此類請求。一旦客戶端找到了主服務器,客戶端就會將所有請求指向它,直到它停止響應,或者直到它表明它不再是主服務器。寫請求通過共識協議傳播到所有副本;當寫操作到達計算單元中的大部分副本時,將確認此類請求。讀請求由主節點單獨滿足。

如果一個副本失敗,並且在幾個小時內沒有恢復,那麼一個簡單的替換系統將從空閒池中選擇一臺新的機器,並在其上啓動鎖服務器二進制文件。然後它更新DNS表,用新副本的IP地址替換失敗副本的IP地址。當前主服務器定期輪詢DNS,並最終注意到更改。然後更新單元格數據庫中的單元格成員列表;通過常規復制協議,該列表在所有成員之間保持一致。同時,新副本從存儲在文件服務器上的備份和活動副本的更新組合中獲得數據庫的最新副本。一旦新副本處理了當前主服務器等待提交的請求,就允許該副本在新主服務器的選舉中投票。

文件、目錄和句柄

Chubby對外提供了一個類似於Unix系統文件系統的目錄樹,可以通過目錄樹來訪問所有的文件。

例如/ls/foo/wombat/pouchls是所有Chubby的名字,foo是Chubby Cell的名字;local是一個特殊的Chubby Cell名,這表示應該使用客戶端的本地Chubby Cell。

命名空間(name space)僅包含文件和目錄,通常稱之爲節點(node)。每一個這樣的節點在其Chubby Cell中只有一個名稱;沒有軟鏈接和硬鏈接。

每個節點都用不同的元數據,包含三個訪問控制列表(access control lists, ACLs)以控制讀權限、寫權限和修改ACL權限。除非重寫ACL,每個文件在創建時都會繼承其父目錄的權限。

ACL本身是位於ACL目錄中的文件。如果一個文件F的寫訪問控制文件的名字是foo,那麼該ACL目錄就會包含一個名爲foo的文件,該文件中包含一個項,該項的值爲bar,那麼用戶bar就擁有了對F文件的寫訪問控制文件。用戶通過RPC系統中內置的機制進行身份驗證。

每個節點的元數據包括四個單調遞增的64位數字,允許客戶端輕鬆地檢測變化:

  1. instance number,大於前面任何具有相同名稱的節點的實例數。
  2. content generation number(只有文件纔有該數字),當文件的內容被寫入時,他的值會增加。
  3. lock generation number,當節點的鎖從自由轉換爲持有時,該值會增加。
  4. ACL generation number,當該節點的ACL名被寫入時該值會增加

Chubby也向外提供了一個64位的文件校驗和來顯示該文件之間是否有差異。

Chubby客戶端打開節點時可以獲得類似於unix文件描述符的Handle,該handle包括:

  1. 檢查防止客戶端創建或猜測句柄的數字,因此只需要在句柄創建時執行完全訪問控制檢查(與UNIX相比,UNIX在打開時檢查其權限位,但不是在每次讀/寫時檢查,因爲文件描述符不能僞造)。
  2. 一個序列號,允許主服務器判斷句柄是由它自己生成的還是由前一個主服務器生成的。
  3. 打開時提供的模式信息,如果舊句柄被提供給新重啓的主程序,則允許主程序重新創建其狀態。

lock and sequencer

Chubby中使用的是讀寫鎖。讀鎖是可共享的,寫鎖是互斥的。同時,Chubby中的鎖是建議性的(advisory),而非強制性的(mandatory)[2],這使得沒有使用鎖的客戶端也可以訪問文件。

在Chubby中,在任何一種模式下獲取鎖都需要寫權限,這樣無特權的讀取器就不能阻止寫入器進行操作。

在分佈式系統中,鎖定是複雜的,因爲通信通常是不確定的,進程可能獨立地失敗。因此,持有鎖L的進程可能發出請求R,但隨後失敗。另一個進程可能獲得L並在R到達目的地之前執行某些操作。如果R隨後到達,則可能在沒有L保護的情況下對其進行操作,並且可能對不一致的數據進行操作。對接收信息的無序問題進行了深入的研究;解決方案包括虛擬時間(virtual time)[3]和虛擬同步(virtual synchrony)[4],它們通過確保按照與每個參與者觀察結果一致的順序處理消息來避免這個問題。

在現有的複雜系統中,將序列號引入到所有的交互中是非常昂貴的。相反,Chubby提供了一種方法,通過這種方法,序列號只能被引入到那些使用鎖的交互中。在任何時候,鎖持有者都可以請求一個序列(sequener),一個不透明的字節串,描述獲得鎖後立即的狀態。它包含鎖的名稱、獲取鎖的模式(獨佔或共享)以及鎖生成編號。如果客戶端希望該操作受到鎖的保護,則將順序器(sequener)傳遞給服務器(例如文件服務器)。接收服務器將測試序列是否仍然有效,是否具有適當的模式;如果沒有,它應該拒絕請求。順序器(sequener)的有效性可以根據服務器的Chubby緩存進行檢查,或者如果服務器不希望維護與Chubby的會話,則根據服務器最近觀察到的順序器(sequener)進行檢查。順序器(sequener)機制只需要在受影響的消息中添加一個字符串,並且很容易向我們的開發人員解釋。

順序器(sequener)協議的實現仍然是一個問題。目前採取了鎖延遲的機制。如果一個客戶端以正常的方式釋放鎖,那麼其他客戶端可以立即獲得鎖。但是,如果一個鎖因爲持有者失敗或變得不可訪問而變得空閒,鎖服務器將在一段稱爲鎖延遲的時間內阻止其他客戶端申請鎖。客戶端可以指定鎖延遲,目前是一分鐘;這個限制可以防止錯誤的客戶端使鎖(以及一些資源)在任意長的時間內不可用。雖然鎖延遲並不完美,但它可以保護未修改的服務器和客戶端免受消息延遲和重啓引起的日常問題。

events

客戶端在創建句柄(handle)時,會訂閱一系列的事件,這些事件包括:

  1. file contents modified
  2. child node added, removed, or modified
  3. Chubby master 通知客戶端某些事件監聽丟失,必須重新掃描數據
  4. a handle (and its lock) has become invalid
  5. lock acquired -- can be used to determine when a primary has been elected
  6. conflicting lock request from another client

緩存

爲了減少讀開銷,Chubby客戶端將文件數據和節點元數據(包括文件缺失)緩存到內存中一致的緩存中,該緩存的一致性使用寫直達(write-through)技術實現。關於write-through讀者應當在計算機體系結構有關CPU緩存的課程中學到,而文章中也沒有提出什麼新鮮的idea,因此不再贅述。需要說明的是,CPU中的各個核心就是論文中的客戶端,CPU的一級或二級緩存就是論文中的內存緩存。

會話與保活 sessions and keepalive

一個Chubby session是一個Chubby cell和一個Chubby client之間的連接,使用週期性握手維護KeepAlive。只要它的會話保持有效,客戶端句柄、鎖和緩存的數據都保持有效,除非Chubby客戶端通知主服務器某些信息失效。

會話建立:客戶端會主動向Chubby Cell的主節點發送連接請求,當會話終止或者會話處於idle狀態(一分鐘內沒有打開的句柄或者進行調用)時,會主動結束會話。

每次會話都會有一段租期,主節點保證在租期內不會主動斷開會話。主節點只會在以下情況中主動終止會話:在創建會話時或者響應KeepAlive RPC時,主節點發生故障。客戶端調用KeepAlive RPC時,主節點會阻塞該調用,直到客戶端的租期接近結束時纔會響應。客戶端在收到應答後會立即再次發送一個KeepAlive RPC。主節點通常會在租期上延長一段時間,以確保客戶端發出的下次KeepAlive RPC到達服務器。KeepAlive應答還用於向客戶端發送事件和緩存失效。主節點允許在交付事件或失效時提前返回KeepAlive。KeepAlive響應上的附帶事件確保了客戶端在沒有確認緩存失效的情況下無法維護會話。

客戶端也會維護一個租期,但是該租期與服務器並不完全相同。因爲客戶端必須考慮網絡時延和服務器時鐘速度。爲了保持一致性,我們要求服務器的時鐘不能比客戶端的時鐘快於已知常數因子。

當客戶端本地租期到期時,客戶端無法確定服務器是否已經關閉了會話。此時,該會話處於危險之中,客戶端將會清空並金庸其緩存。客戶端會等待一個寬限期,如果在寬限期內成功響應keepalive,那麼會重新使能緩存。否則,客戶端認爲會話已經過期。

chubby庫會通過危險事件(jeopardy event)通知客戶端寬限期過期。

如果客戶端持有節點上的句柄H,並且由於關聯的會話已過期而導致對H的任何操作都失敗,那麼對H的所有後續操作(除了Close()和Poison())都會以同樣的方式失敗。客戶端可以使用這一點來保證網絡和服務器中斷只會導致操作序列的一個後綴丟失,而不是任意的子序列丟失,從而允許將複雜的更改標記爲已提交的最終寫入。

fails-over

master一旦丟掉了master資格,就會清空掉session、handle、lock的內存狀態。但session timer會直到新master產生才結束(不立即讓client的session失效)。這樣如果client能在grace period與新master建立通信,它的所有現存session都不會受影響。在grace period中,client會阻塞住應用調用,以避免應用看到不一致的數據。新master產生後部分通過讀取穩定存儲在磁盤上的數據(通過正常的數據庫複製協議進行復制),部分通過從客戶端獲取狀態,部分通過保守的假設實現。數據庫記錄每個會話、持有的鎖和臨時文件。

其他機制

Database

Chubby的第一個版本使用Berkeley DB作爲它的數據庫。

backup

每隔一段事件,每個Chubby Cell的master都會將數據庫的快照寫入不同的GFS進行災備。

mirroring

Chubby允許將一組文件從一個Chubby Cell鏡像到另一個Chubby Cell。如果沒有網絡問題,變化會在不到一秒鐘的時間內反映在全球幾十個鏡像上。如果一個鏡像不可達,它將保持不變,直到連接恢復。然後通過比較它們的校驗和來識別更新的文件。

參考文獻


  1. BURROWS M. The Chubby Lock Service for Loosely-Coupled Distributed Systems[J]. ↩︎

  2. 建議性鎖和強制性鎖機制下的鎖 ↩︎

  3. JEFFERSON, D. Virtual time. ACM TOPLAS, 3 (1985),
    404–425. ↩︎

  4. BIRMAN, K. P., AND JOSEPH, T. A. Exploiting virtual
    synchrony in distributed systems. In 11th SOSP (1987),
    pp. 123–138. ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章