聊聊Zookeeper之會話機制Session

什麼是Zookeeper的會話機制

我們在服務器啓動Zookeeper的時候能得知,ZK服務端對外默認端口是2181。而客戶端連接到服務端上,其本質其實就是一個TCP連接(長連接) ,當連接正式建立起來的時候,就開起來該次會話的生命週期了。有了會話之後,後續的請求發送,迴應,心跳檢測等機制都是基於會話來實現的。那對於ZK的服務端來說,如何維護管理這些會話,就是本文要聊的內容啦~

Session相關的基本概念

當連接建立的時候,Session就已經建立起來,與這個過程相關的有三個重要的值:

  • SessionID:會話的唯一標識,由ZK來分配

  • TimeOut:會話超時時間。在客戶端與服務端連接的期間,如果因爲某些原因斷開了連接(如網絡中斷等等),該次會話以及其相關的臨時節點不會被馬上刪除,而是等待TimeOut耗盡之後,若客戶端沒有重連上來,那本次會話纔會失效,相關的一些臨時節點也會被刪除

  • Expiration Time:TimeOut是一個相對時間,而Expiration Time則是在時間軸上的一個絕對過期時間。可能你也會想到,一個比較通用的計算方法就是:
    ExpirationTime=CurrentTime()+TimeOut() ExpirationTime = CurrentTime(當前時間) + TimeOut(超時時間)
    這樣算出來的時間最準確。但ZK可不是這麼算的,它用的是
    ExpirationTime=CurrentTime+SessionTimeOut ExpirationTime_ = CurrentTime + SessionTimeOut

    ExpirationTime=(ExpirationTime/ExpirationInterval+1)ExpirationInterval ExpirationTime = ( ExpirationTime_/ExpirationInterval + 1 ) * ExpirationInterval
    ExpirationInterval默認值爲2000毫秒,至於爲什麼要這樣算,稍後講分桶策略的時候再詳細聊一下叭~

順便貼一下SessionId生成的源碼,SessionId的生成和兩個東西相關聯,一個是時間戳,一個是機器id

/**其中id是機器id**/
public static long initializeNextSession(long id) {
        long nextSid = 0;
        nextSid = (System.currentTimeMillis() << 24) >>> 8;
        nextSid =  nextSid | (id <<56);
        return nextSid;
}

分桶機制

Session是由ZK服務端來進行管理的,一個服務端可以爲多個客戶端服務,也就是說,有多個Session,那這些Session是怎麼樣被管理的呢?而分桶機制可以說就是其管理的一個手段。ZK服務端會維護着一個個"桶",然後把Session們分配到一個個的桶裏面。而這個區分的維度,就是ExpirationTime

img

爲什麼要如此區分呢?因爲ZK的服務端會在運行期間定時地對會話進行超時檢測,如果不對Session進行維護的話,那在檢測的時候豈不是要遍歷所有的Session?這顯然不是一個好辦法,所以才以超時時間爲維度來存放Session,這樣在檢測的時候,只需要掃描對應的桶就可以了

那這樣的話,新的問題就來了:每個Session的超時時間是一個很分散的值,假設有1000個Session,很可能就會有1000個不同的超時時間,進而有1000個桶,這樣有啥意義嗎?這就要回頭看一下ExpirationTime的計算公式了,再貼一下:
ExpirationTime=CurrentTime+SessionTimeOut ExpirationTime_ = CurrentTime + SessionTimeOut

ExpirationTime=(ExpirationTime/ExpirationInterval+1)ExpirationInterval ExpirationTime = ( ExpirationTime_/ExpirationInterval + 1 ) * ExpirationInterval

可以看到,最終得到的ExpirationTime是ExpirationInterval的倍數,而ExpirationInterval就是ZK服務端定時檢查過期Session的頻率,默認爲2000毫秒。所以說,每個Session的ExpirationTime最後都是一個近似值,是ExpirationInterval的倍數,這樣的話,ZK在進行掃描的時候,只需要掃描一個桶即可。

另外讓過期時間是ExpirationInterval的倍數還有一個好處就是,讓檢查時間和每個Session的過期時間在一個時間節點上。否則的話就會出現一個問題:ZK檢查完畢的1毫秒後,就有一個Session新過期了,這種情況肯定是不好。

Session激活(續約)

在客戶端與服務端完成連接之後生成過期時間,這個值並不是一直不變的,而是會隨着客戶端與服務端的交互來更新。過期時間的更新,當然就伴隨着Session在桶上的遷移

最簡單的一點,客戶端每向服務端發送請求,包括讀請求和寫請求,都會觸發一次激活,因爲這預示着客戶端處於活躍狀態

而如果客戶端一直沒有讀寫請求,那麼它在TimeOut的三分之一時間內沒有發送過請求的話,那麼客戶端會發送一次PING,來觸發Session的激活。當然,如果客戶端直接斷開連接的話,那麼TimeOut結束後就會被服務端掃描到然後進行清楚了

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