【分佈式】Zookeeper會話

【分佈式】Zookeeper會話

一、前言

  前面分析了Zookeeper客戶端的細節,接着繼續學習Zookeeper中的一個非常重要的概念:會話。

二、會話

  客戶端與服務端之間任何交互操作都與會話息息相關,如臨時節點的生命週期、客戶端請求的順序執行、Watcher通知機制等。Zookeeper的連接與會話就是客戶端通過實例化Zookeeper對象來實現客戶端與服務端創建並保持TCP連接的過程.

  2.1 會話狀態

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

  一旦客戶端開始創建Zookeeper對象,那麼客戶端狀態就會變成CONNECTING狀態,同時客戶端開始嘗試連接服務端,連接成功後,客戶端狀態變爲CONNECTED,通常情況下,由於斷網或其他原因,客戶端與服務端之間會出現斷開情況,一旦碰到這種情況,Zookeeper客戶端會自動進行重連服務,同時客戶端狀態再次變成CONNCTING,直到重新連上服務端後,狀態又變爲CONNECTED,在通常情況下,客戶端的狀態總是介於CONNECTING和CONNECTED之間。但是,如果出現諸如會話超時、權限檢查或是客戶端主動退出程序等情況,客戶端的狀態就會直接變更爲CLOSE狀態。

  2.2 會話創建

  Session是Zookeeper中的會話實體,代表了一個客戶端會話,其包含了如下四個屬性

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

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

  3. TickTime。下次會話超時時間點,爲了便於Zookeeper對會話實行"分桶策略"管理,同時爲了高效低耗地實現會話的超時檢查與清理,Zookeeper會爲每個會話標記一個下次會話超時時間點,其值大致等於當前時間加上TimeOut。

  4. isClosing。標記一個會話是否已經被關閉,當服務端檢測到會話已經超時失效時,會將該會話的isClosing標記爲"已關閉",這樣就能確保不再處理來自該會話的心情求了。

  Zookeeper爲了保證請求會話的全局唯一性,在SessionTracker初始化時,調用initializeNextSession方法生成一個sessionID,之後在Zookeeper運行過程中,會在該sessionID的基礎上爲每個會話進行分配,初始化算法如下

複製代碼
public static long initializeNextSession(long id) {
    long nextSid = 0;
    // 無符號右移8位使爲了避免左移24後,再右移8位出現負數而無法通過高8位確定sid值
    nextSid = (System.currentTimeMillis() << 24) >>> 8;
    nextSid = nextSid | (id << 56);
    return nextSid;
}
複製代碼

  其中的id表示配置在myid文件中的值,通常是一個整數,如1、2、3。該算法的高8位確定了所在機器,後56位使用當前時間的毫秒錶示進行隨機。SessionTracker是Zookeeper服務端的會話管理器,負責會話的創建、管理和清理等工作。

  2.3 會話管理

  Zookeeper的會話管理主要是通過SessionTracker來負責,其採用了分桶策略(將類似的會話放在同一區塊中進行管理)進行管理,以便Zookeeper對會話進行不同區塊的隔離處理以及同一區塊的統一處理。

  Zookeeper將所有的會話都分配在不同的區塊一種,分配的原則是每個會話的下次超時時間點(ExpirationTime)。ExpirationTime指該會話最近一次可能超時的時間點。同時,Zookeeper Leader服務器在運行過程中會定時地進行會話超時檢查,時間間隔是ExpirationInterval,默認爲tickTime的值,ExpirationTime的計算時間如下

  ExpirationTime = ((CurrentTime + SessionTimeOut) / ExpirationInterval + 1) * ExpirationInterval

  會了保持客戶端會話的有效性,客戶端會在會話超時時間過期範圍內向服務端發送PING請求來保持會話的有效性(心跳檢測)。同時,服務端需要不斷地接收來自客戶端的心跳檢測,並且需要重新激活對應的客戶端會話,這個重新激活過程稱爲TouchSession。會話激活不僅能夠使服務端檢測到對應客戶端的存貨性,同時也能讓客戶端自己保持連接狀態,其流程如下  


  如上圖所示,整個流程分爲四步

  1. 檢查該會話是否已經被關閉。若已經被關閉,則直接返回即可。

  2. 計算該會話新的超時時間ExpirationTime_New。使用上面提到的公式計算下一次超時時間點。

  3. 獲取該會話上次超時時間ExpirationTime_Old。計算該值是爲了定位其所在的區塊。

  3. 遷移會話。將該會話從老的區塊中取出,放入ExpirationTime_New對應的新區塊中。

  在上面會話激活過程中,只要客戶端發送心跳檢測,服務端就會進行一次會話激活,心跳檢測由客戶端主動發起,以PING請求形式向服務端發送,在Zookeeper的實際設計中,只要客戶端有請求發送到服務端,那麼就會觸發一次會話激活,以下兩種情況都會觸發會話激活。

  1. 客戶端向服務端發送請求,包括讀寫請求,就會觸發會話激活。

  2. 客戶端發現在sessionTimeout/3時間內尚未和服務端進行任何通信,那麼就會主動發起PING請求,服務端收到該請求後,就會觸發會話激活。

  對於會話的超時檢查而言,Zookeeper使用SessionTracker來負責,SessionTracker使用單獨的線程(超時檢查線程)專門進行會話超時檢查,即逐個一次地對會話桶中剩下的會話進行清理。如果一個會話被激活,那麼Zookeeper就會將其從上一個會話桶遷移到下一個會話桶中,如ExpirationTime 1 的session n 遷移到ExpirationTime n 中,此時ExpirationTime 1中留下的所有會話都是尚未被激活的,超時檢查線程就定時檢查這個會話桶中所有剩下的未被遷移的會話,超時檢查線程只需要在這些指定時間點(ExpirationTime 1、ExpirationTime 2...)上進行檢查即可,這樣提高了檢查的效率,性能也非常好。

  2.4 會話清理

  當SessionTracker的會話超時線程檢查出已經過期的會話後,就開始進行會話清理工作,大致可以分爲如下七步。

  1. 標記會話狀態爲已關閉。由於會話清理過程需要一段時間,爲了保證在此期間不再處理來自該客戶端的請求,SessionTracker會首先將該會話的isClosing標記爲true,這樣在會話清理期間接收到該客戶端的心情求也無法繼續處理了。

  2. 發起會話關閉請求。爲了使對該會話的關閉操作在整個服務端集羣都生效,Zookeeper使用了提交會話關閉請求的方式,並立即交付給PreRequestProcessor進行處理。

  3. 收集需要清理的臨時節點。一旦某個會話失效後,那麼和該會話相關的臨時節點都需要被清理,因此,在清理之前,首先需要將服務器上所有和該會話相關的臨時節點都整理出來。Zookeeper在內存數據庫中會爲每個會話都單獨保存了一份由該會話維護的所有臨時節點集合,在Zookeeper處理會話關閉請求之前,若正好有以下兩類請求到達了服務端並正在處理中。

    · 節點刪除請求,刪除的目標節點正好是上述臨時節點中的一個。

    · 臨時節點創建請求,創建的目標節點正好是上述臨時節點中的一個。

  對於第一類請求,需要將所有請求對應的數據節點路徑從當前臨時節點列表中移出,以避免重複刪除,對於第二類請求,需要將所有這些請求對應的數據節點路徑添加到當前臨時節點列表中,以刪除這些即將被創建但是尚未保存到內存數據庫中的臨時節點。

  4. 添加節點刪除事務變更。完成該會話相關的臨時節點收集後,Zookeeper會逐個將這些臨時節點轉換成"節點刪除"請求,並放入事務變更隊列outstandingChanges中。

  5. 刪除臨時節點。FinalRequestProcessor會觸發內存數據庫,刪除該會話對應的所有臨時節點。

  6. 移除會話。完成節點刪除後,需要將會話從SessionTracker中刪除。

  7. 關閉NIOServerCnxn。最後,從NIOServerCnxnFactory找到該會話對應的NIOServerCnxn,將其關閉。

  2.5 重連

  當客戶端與服務端之間的網絡連接斷開時,Zookeeper客戶端會自動進行反覆的重連,直到最終成功連接上Zookeeper集羣中的一臺機器。此時,再次連接上服務端的客戶端有可能處於以下兩種狀態之一

  1. CONNECTED。如果在會話超時時間內重新連接上集羣中一臺服務器 。

  2. EXPIRED。如果在會話超時時間以外重新連接上,那麼服務端其實已經對該會話進行了會話清理操作,此時會話被視爲非法會話。

  在客戶端與服務端之間維持的是一個長連接,在sessionTimeout時間內,服務端會不斷地檢測該客戶端是否還處於正常連接,服務端會將客戶端的每次操作視爲一次有效的心跳檢測來反覆地進行會話激活。因此,在正常情況下,客戶端會話時一直有效的。然而,當客戶端與服務端之間的連接斷開後,用戶在客戶端可能主要看到兩類異常:CONNECTION_LOSS(連接斷開)和SESSION_EXPIRED(會話過期)

  1. CONNECTION_LOSS。此時,客戶端會自動從地址列表中重新逐個選取新的地址並嘗試進行重新連接,直到最終成功連接上服務器。若客戶端在setData時出現了CONNECTION_LOSS現象,此時客戶端會收到None-Disconnected通知,同時會拋出異常。應用程序需要捕捉異常並且等待Zookeeper客戶端自動完成重連,一旦重連成功,那麼客戶端會收到None-SyncConnected通知,之後就可以重試setData操作。

  2. SESSION_EXPIRED。客戶端與服務端斷開連接後,重連時間耗時太長,超過了會話超時時間限制後沒有成功連上服務器,服務器會進行會話清理,此時,客戶端不知道會話已經失效,狀態還是DISCONNECTED,如果客戶端重新連上了服務器,此時狀態爲SESSION_EXPIRED,用於需要重新實例化Zookeeper對象,並且看應用的複雜情況,重新恢復臨時數據。

  3. SESSION_MOVED。客戶端會話從一臺服務器轉移到另一臺服務器,即客戶端與服務端S1斷開連接後,重連上了服務端S2,此時會話就從S1轉移到了S2。當多個客戶端使用相同的sessionId/sessionPasswd創建會話時,會收到SessionMovedException異常。因爲一旦有第二個客戶端連接上了服務端,就被認爲是會話轉移了。

三、總結

  本篇博文介紹了Zookeeper會話的相關細節,通過本篇的學習理解了會話的細節,也謝謝各位園友的觀看~

文章轉自:https://www.cnblogs.com/leesf456/p/6103870.html

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