1. 程式人生 > >【zookeeper之八】Zookeeper會話

【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會話的相關細節,通過本篇的學習理解了會話的細節,也謝謝各位園友的觀看~

出處:http://www.cnblogs.com/leesf456/