ZooKeeper Session分析

在ZooKeeper客戶端與服務端成功完成建立連接後,就建立了一個會話。ZooKeeper會話在整個運行期間的生命週期中,會在不同的會話狀態之間進行切換,這些狀態一般可以分爲CONNECTING、CONNECTED、RECONNECTING、RECONNECTED和CLOSE等。

Session 是ZooKeeper中最重要的概念之一。它包括4個基本屬性:

sessionID:會話ID,唯一標識一個會話,每次客戶端創建新會話的時候,ZooKeeper都會爲其分配一個全局唯一的sessionID。

TimeOut:會話超時時間。客戶端在構造ZooKeeper實例的時候,會配置一個sessionTimeOut參數用於指定會話超時時間。ZooKeeper客戶端向服務器發送這個超時時間後,服務器會根據自己的超時時間限制最終確定會話的超時時間。

TickTime:下次會話超時時間點。爲了便於ZooKeeper對會話實行“分桶策略”管理,同時也是爲了高效低耗地實現的超時檢測與清理,ZooKeeper會爲每個會話標識一個下次會話超時時間。

isClosing:該屬性用於標記一個會話是否被關閉。通常當服務端檢測到一個會話已經超時失效的時候,會將該會話的isClosing屬性標記爲“已關閉”,這樣就能確保不再處理來自該會話的新請求了。

SessionTracker

SessionTracker是ZooKeeper服務端的會話管理器,負責會話的創建、管理和清理工作。每一個會話在SessionTracker內部都保留三份,具體如下。

sessionById:這是一個HashMap<Long,SessionImpl>類型的數據結構,用於根據sessionID來管理Session實體。

sessionWithTimeout:這是一個ConcurrentHashMap<Long,Integer>類型的數據結構,用於根據sessionID來管理會話的超時時間。該數據結構和ZooKeeper內存數據庫相連通,會被定期持久化到快照文件中。

sessionSets:這是一個HashMap<Long,SessionSet>類型的數據結構,用於根據下次會話超時時間點來歸檔會話,便於進行會話管理和超時檢查。在下文“分桶策略”會話管理的介紹中,我們還會對該數據結構進行詳細講解。

創建連接

服務端對於客戶端的“會話創建”處理,大體可以分爲四大步驟,分別是處理ConnectRequest請求、會話創建、處理器鏈路處理和會話響應。在ZooKeeper服務端首先將會由NIOServerCnxn來負責接收來自客戶端的“會話創建”請求,並反序列化出ConnectRequest請求,然後根據ZooKeeper服務端的配置完成會話超時時間的協商。隨後SessionTracker將會爲該會話分配一個sessionID,並將其註冊到sessionById和sessionWithTimeout中,同時進行會話激活。之後,該“會話請求”還會在ZooKeeper服務端的各個請求處理器之間進行順序流轉,最終完成會話的創建。

接下來分析一下session狀態之間的轉換:

0?wx_fmt=png

Session從NOT_CONNECTED狀態開始,並隨着Zookeeper客戶端初始化,轉移到CONNECTING狀態(在圖1中的箭頭1)。 正常情況下,客戶端會與Zookeeper服務器連接成功,並且轉移到CONNECTED狀態(箭頭2)。當客戶端失去了與ZooKeeper服務器的連接或者不能聽到服務器,它會轉移回CONNECTING(箭頭3),並且嘗試尋找另一個ZooKeeper服務器。如果它能找到另一個服務器或者重新連接到之前的服務器,並確認了這個Session仍然有效,它會轉移回CONNECTED狀態。否則,它會定義這個Session失效,並轉移到CLOSED(箭頭4)。應用可以顯示關閉Session。(箭頭4和5)

如果客戶端因爲超時和服務器斷開連接,它會保持在CONNECTING狀態。如果這個斷開是因爲客戶端和Zookeepe集羣的網絡中斷,它將保持在CONNECTING狀態直到它顯示地關閉Session,或者網絡中斷恢復後客戶端從Zookeeper服務器聽到了Session超時消息。我們設計這樣的行爲是因爲只有Zookeeper集羣負責定義Session失效,而不是客戶端。客戶端不能定義Session失效,直到聽到Zookeeper Session超時消息。然而,客戶端可以選擇主動關閉這個Session。

在創建Session時,需要設置Session Timeout這個重要參數。這是Zookeeper服務允許一個Session在定義它失效之前的時間。如果服務在時間t內不能看到與一個Session關聯的消息,它將定義這個Session失效。如果客戶端在1/3 t時間內沒有聽到任何從服務器過來的消息,它將發送一個心跳消息給服務器。在(2/3)t時間, Zookeeper客戶端開始尋找另一個Zookeeper服務器,並且它有另外的(1/3)t的時間尋找。

客戶端將連接哪一個服務器

在Quorum模式,一個客戶端擁有多個服務器可以連接。然而在Standalone模式,它必須嘗試重新有效地連接到那個唯一的服務器。在Quorum模式,應該會傳一個服務器列表到客戶端,客戶端從中選擇一個連接。

當嘗試連接另一個服務器時,很重要的一點是這個服務器的ZooKeeper狀態至少要和客戶端已經觀察到的最近ZooKeeper狀態是一樣新的。客戶端不能連接到一個這樣的服務器。它沒有看到客戶端可能已經看到的更新。Zookeeper通過在服務中排序更新操作來決定新鮮程度(Freshness)。每一個對Zookeeper佈局狀態的改動操作相對於所有其它執行的更新操作都是全序的,所以如果一個客戶端已經在位置i觀察到一個更新,它不能連接一個僅看到i' < i的服務器。在ZooKeeper的實現中,系統分配給每個更新操作一個事務ID來建立這個順序。


更多精彩內容,歡迎關注微信公衆號:Java小筆記(ijavanote)


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