溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

發(fā)布時(shí)間:2021-12-23 18:04:01 來源:億速云 閱讀:130 作者:柒染 欄目:云計(jì)算

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

客戶端API簡單使用

0.1 demo案例1

一個(gè)最簡單的demo如下:

public class ZookeeperConstructorSimple implements Watcher{

    private static CountDownLatch connectedSemaphone=new CountDownLatch(1);

    public static void main(String[] args) throws IOException {
        ZooKeeper zooKeeper=new ZooKeeper("127.0.0.1:2181",5000,new ZookeeperConstructorSimple());
        System.out.println(zooKeeper.getState());
        try {
            connectedSemaphone.await();
        } catch (Exception e) {}
        System.out.println("ZooKeeper session established");
        System.out.println("sessionId="+zooKeeper.getSessionId());
        System.out.println("password="+zooKeeper.getSessionPasswd());
    }

    @Override
    public void process(WatchedEvent event) {
        System.out.println("my ZookeeperConstructorSimple watcher Receive watched event:"+event);
        if(KeeperState.SyncConnected==event.getState()){
            connectedSemaphone.countDown();
        }
    }

}

使用的maven依賴如下:

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.6</version>
</dependency>

對(duì)于目前來說,ZooKeeper的服務(wù)器端代碼和客戶端代碼還是混在一起的,估計(jì)日后能改吧。

使用的ZooKeeper的構(gòu)造函數(shù)有三個(gè)參數(shù)構(gòu)成

  • ZooKeeper集群的服務(wù)器地址列表

    該地址是可以填寫多個(gè)的,以逗號(hào)分隔。如"127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183",那客戶端連接的時(shí)候到底是使用哪一個(gè)呢?先隨機(jī)打亂,然后輪詢著用,后面再詳細(xì)介紹。

  • sessionTimeout

    最終會(huì)引出三個(gè)時(shí)間設(shè)置:和服務(wù)器端協(xié)商后的sessionTimeout、readTimeout、connectTimeout

    服務(wù)器端使用協(xié)商后的sessionTimeout:即超過該時(shí)間后,客戶端沒有向服務(wù)器端發(fā)送任何請(qǐng)求(正常情況下客戶端會(huì)每隔一段時(shí)間發(fā)送心跳請(qǐng)求,此時(shí)服務(wù)器端會(huì)從新計(jì)算客戶端的超時(shí)時(shí)間點(diǎn)的),則服務(wù)器端認(rèn)為session超時(shí),清理數(shù)據(jù)。此時(shí)客戶端的ZooKeeper對(duì)象就不再起作用了,需要再重新new一個(gè)新的對(duì)象了。

    客戶端使用connectTimeout、readTimeout分別用于檢測(cè)連接超時(shí)和讀取超時(shí),一旦超時(shí),則該客戶端認(rèn)為該服務(wù)器不穩(wěn)定,就會(huì)從新連接下一個(gè)服務(wù)器地址。

  • Watcher

    作為ZooKeeper對(duì)象一個(gè)默認(rèn)的Watcher,用于接收一些事件通知。如和服務(wù)器連接成功的通知、斷開連接的通知、Session過期的通知等。

同時(shí)我們可以看到,一旦和ZooKeeper服務(wù)器連接建立成功,就會(huì)獲取服務(wù)器端分配的sessionId和password,如下:

sessionId=94249128002584594
password=[B@4de3aaf6

下面就通過源碼來詳細(xì)說明這個(gè)建立連接的過程。

1 客戶端的建立連接的過程

1.1 大體連接過程概述

首先與ZooKeeper服務(wù)器建立連接,有兩層連接要建立。

  • 客戶端與服務(wù)器端的TCP連接

  • 在TCP連接的基礎(chǔ)上建立session關(guān)聯(lián)

建立TCP連接之后,客戶端發(fā)送ConnectRequest請(qǐng)求,申請(qǐng)建立session關(guān)聯(lián),此時(shí)服務(wù)器端會(huì)為該客戶端分配sessionId和密碼,同時(shí)開啟對(duì)該session是否超時(shí)的檢測(cè)。

當(dāng)在sessionTimeout時(shí)間內(nèi),即還未超時(shí),此時(shí)TCP連接斷開,服務(wù)器端仍然認(rèn)為該sessionId處于存活狀態(tài)。此時(shí),客戶端會(huì)選擇下一個(gè)ZooKeeper服務(wù)器地址進(jìn)行TCP連接建立,TCP連接建立完成后,拿著之前的sessionId和密碼發(fā)送ConnectRequest請(qǐng)求,如果還未到該sessionId的超時(shí)時(shí)間,則表示自動(dòng)重連成功,對(duì)客戶端用戶是透明的,一切都在背后默默執(zhí)行,ZooKeeper對(duì)象是有效的。

如果重新建立TCP連接后,已經(jīng)達(dá)到該sessionId的超時(shí)時(shí)間了(服務(wù)器端就會(huì)清理與該sessionId相關(guān)的數(shù)據(jù)),則返回給客戶端的sessionTimeout時(shí)間為0,sessionid為0,密碼為空字節(jié)數(shù)組??蛻舳私邮盏皆摂?shù)據(jù)后,會(huì)判斷協(xié)商后的sessionTimeout時(shí)間是否小于等于0,如果小于等于0,則使用eventThread線程先發(fā)出一個(gè)KeeperState.Expired事件,通知相應(yīng)的Watcher,然后結(jié)束EventThread線程的循環(huán),開始走向結(jié)束。此時(shí)ZooKeeper對(duì)象就是無效的了,必須要重新new一個(gè)新的ZooKeeper對(duì)象,分配新的sessionId了。

1.2 ZooKeeper對(duì)象

它是面向用戶的,提供一些操作API。

它又兩個(gè)重要的屬性:

  • ClientCnxn cnxn:負(fù)責(zé)所有的ZooKeeper節(jié)點(diǎn)操作的執(zhí)行

  • ZKWatchManager watchManager:負(fù)責(zé)維護(hù)某個(gè)path上注冊(cè)的Watcher

如創(chuàng)建某個(gè)node操作(同步方式):

ZooKeeper對(duì)象負(fù)責(zé)創(chuàng)建出Request,并交給ClientCnxn來執(zhí)行,ZooKeeper對(duì)象再對(duì)返回結(jié)果進(jìn)行處理。

如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

同步方式提交一個(gè)請(qǐng)求后,開始循環(huán)判斷該請(qǐng)求包的狀態(tài)是否結(jié)束,即處于阻塞狀態(tài),一旦結(jié)束則繼續(xù)往下走下去,返回結(jié)果。異步方式則提交一個(gè)請(qǐng)求后,直接返回,對(duì)結(jié)果的處理邏輯包含在回調(diào)函數(shù)中。一旦該對(duì)該請(qǐng)求包響應(yīng)完畢,則取出回調(diào)函數(shù)執(zhí)行相應(yīng)的回調(diào)方法。

ZooKeeper對(duì)象主要封裝用戶的請(qǐng)求以及處理響應(yīng)等操作。用戶請(qǐng)求的執(zhí)行全部交給ClientCnxn來執(zhí)行,那我們就詳細(xì)看下ClientCnxn的來源及大體內(nèi)容。

先看看ClientCnxn是怎么來的:

如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

  • 第一步:為ZKWatchManager watchManager設(shè)置一個(gè)默認(rèn)的Watcher

  • 第二步:將連接字符串信息交給ConnectStringParser進(jìn)行解析

    連接字符串比如: "192.168.12.1:2181,192.168.12.2:2181,192.168.12.3:2181/root"

  • 得到兩個(gè)數(shù)據(jù)String chrootPath默認(rèn)的跟路徑和ArrayList<InetSocketAddress> serverAddresses即多個(gè)host和port信息。

  • 第三步:根據(jù)上述解析的host和port列表結(jié)果,創(chuàng)建一個(gè)HostProvider

    有了ConnectStringParser的解析結(jié)果,為什么還需要一個(gè)HostProvider再來包裝下呢?主要是為將來留下擴(kuò)展的余地

    來看下HostProvider的詳細(xì)接口介紹:

    如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

    HostProvider主要負(fù)責(zé)不斷的對(duì)外提供可用的ZooKeeper服務(wù)器地址,這些服務(wù)器地址可以是從一個(gè)url中加載得來或者其他途徑得來。同時(shí)對(duì)于不同的ZooKeeper客戶端,給出就近的ZooKeeper服務(wù)器地址等。

  • 有三個(gè)屬性,一個(gè)就是服務(wù)器地址列表(經(jīng)過如下方式隨機(jī)打亂了):

    Collections.shuffle(this.serverAddresses)

    另外兩個(gè)屬性用于標(biāo)記,下面來具體看下,StaticHostProvider是如何實(shí)現(xiàn)不斷的對(duì)外提供ZooKeeper服務(wù)器地址的:

    如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

    代碼也很簡單,就是在打亂的服務(wù)器地址列表中,不斷地遍歷,到頭之后,在從0開始。

    上面的spinDelay是個(gè)什么情況呢?

    正常情況下,currentIndex先加1,然后返回currentIndex+1的地址,當(dāng)該地址連接成功后會(huì)執(zhí)行onConnected方法,即lastIndex = currentIndex了。然而當(dāng)返回的currentIndex+1的地址連接不成功,繼續(xù)嘗試下一個(gè),仍不成功,仍繼續(xù)下一個(gè),就會(huì)遇到currentIndex=lastIndex的情況,此時(shí)即輪詢了一遍,仍然沒有一個(gè)地址能夠連接上,此時(shí)的策略就是先暫停休息休息,然后再繼續(xù)。

  • 第四步:為創(chuàng)建ClientCnxn準(zhǔn)備參數(shù)并創(chuàng)建ClientCnxn。

    首先是通過getClientCnxnSocket()獲取一個(gè)ClientCnxnSocket。來看下ClientCnxnSocket是主要做什么工作的:

    A ClientCnxnSocket does the lower level communication with a socket implementation. This code has been moved out of ClientCnxn so that a Netty implementation can be provided as an alternative to the NIO socket code.

    專門用于負(fù)責(zé)socket通信的,把一些公共部分抽象出來,其他的留給不同的實(shí)現(xiàn)者來實(shí)現(xiàn)。如可以選擇默認(rèn)的ClientCnxnSocketNIO,也可以使用netty等。


  • 首先獲取系統(tǒng)參數(shù)"zookeeper.clientCnxnSocket",如果沒有的話,使用默認(rèn)的ClientCnxnSocketNIO,所以我們可以通過指定該參數(shù)來替換默認(rèn)的實(shí)現(xiàn)。

    參數(shù)準(zhǔn)備好了,ClientCnxn是如何來創(chuàng)建的呢?

    如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

    首先就是保存一些對(duì)象參數(shù),此時(shí)的sessionId和sessionPasswd都還沒有。然后就是兩個(gè)timeout參數(shù):connectTimeout和readTimeout。在ClientCnxn的發(fā)送和接收數(shù)據(jù)的線程中,會(huì)不斷的檢測(cè)連接超時(shí)和讀取超時(shí),一旦出現(xiàn)超時(shí),就認(rèn)為服務(wù)不穩(wěn)定,需要更換服務(wù)器,就會(huì)從HostProvider中獲取下一個(gè)服務(wù)器地址進(jìn)行連接。

    最后就是兩個(gè)線程,一個(gè)事件線程即EventThread,一個(gè)發(fā)送和接收socket數(shù)據(jù)的線程即SendThread。

    事件線程EventThread呢就是從一個(gè)事件隊(duì)列中不斷取出事件并進(jìn)行處理

  • 看下具體的處理過程,主要分成兩種情況,一種就是我們注冊(cè)的watch事件,另一種就是處理異步回調(diào)函數(shù):

    如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

    可以看到這里就是觸發(fā)我們注冊(cè)Watch的,還有觸發(fā)上文提到的異步回調(diào)的情況的。

    明白了EventThread是如何來處理事件的,需要知道這些事件是如何來的:

  • 對(duì)外提供了三個(gè)方法來添加不同類型的事件,如SendThread線程就會(huì)調(diào)用這三個(gè)方法來添加事件。其中對(duì)于事件通知,會(huì)首先根據(jù)ZKWatchManager watchManager來獲取關(guān)心該事件的所有Watcher,然后觸發(fā)他們。

    再來看看SendThread的工作內(nèi)容:

    sendThread = new SendThread(clientCnxnSocket); 把傳遞給ClientCnxn的clientCnxnSocket,再傳遞給SendThread,讓它服務(wù)于SendThread。

    在SendThread的run方法中,有一個(gè)while循環(huán),不斷的做著以下幾件事:

    • 任務(wù)1:不斷檢測(cè)clientCnxnSocket是否和服務(wù)器處于連接狀態(tài),如果是未連接狀態(tài),則從hostProvider中取出一個(gè)服務(wù)器地址,使用clientCnxnSocket進(jìn)行連接。

      如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

      和服務(wù)器建立連接成功后,開始發(fā)送ConnectRequest請(qǐng)求,把該請(qǐng)求放到outgoingQueue請(qǐng)求隊(duì)列中,等待被發(fā)送給服務(wù)器


    • 任務(wù)2:檢測(cè)是否超時(shí):當(dāng)處于連接狀態(tài)時(shí),檢測(cè)是否讀超時(shí),當(dāng)處于未連接狀態(tài)時(shí),檢測(cè)是否連接超時(shí)

      如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

      一旦超時(shí),則拋出SessionTimeoutException,然后看下是如何處理呢?


    • 可以看到一旦發(fā)生超時(shí)異?;蛘咂渌惓?,都會(huì)進(jìn)行清理,并設(shè)置連接狀態(tài)為未連接,然后發(fā)送Disconnected事件。至此又會(huì)進(jìn)入任務(wù)1的流程

    • 任務(wù)3:不斷的發(fā)送ping通知,服務(wù)器端每接收到ping請(qǐng)求,就會(huì)從當(dāng)前時(shí)間重新計(jì)算session過期時(shí)間,所以當(dāng)客戶端按照一定時(shí)間間隔不斷的發(fā)送ping請(qǐng)求,就能保證客戶端的session不會(huì)過期。發(fā)送時(shí)間間隔如下:

      如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

      clientCnxnSocket.getIdleSend():是最后一次發(fā)送數(shù)據(jù)包的時(shí)間與當(dāng)前時(shí)間的間隔。當(dāng)readTimeout的時(shí)間已經(jīng)過去一半多了,都沒有發(fā)送數(shù)據(jù)包的話,則執(zhí)行一次Ping發(fā)送?;蛘哌^去MAX_SEND_PING_INTERVAL(10s)都還沒有發(fā)送數(shù)據(jù)包的話,則執(zhí)行一次Ping發(fā)送。

    • ping發(fā)送的內(nèi)容只有請(qǐng)求頭OpCode.ping的標(biāo)示,其他都為空。發(fā)送ping請(qǐng)求,也是把該請(qǐng)求放到outgoingQueue發(fā)送隊(duì)列中,等待被執(zhí)行。

    • 任務(wù)4:執(zhí)行IO操作,即發(fā)送請(qǐng)求隊(duì)列中的請(qǐng)求和讀取服務(wù)器端的響應(yīng)數(shù)據(jù)。

      如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

      首先從outgoingQueue請(qǐng)求隊(duì)列中取出第一個(gè)請(qǐng)求,然后進(jìn)行序列化,然后使用socket進(jìn)行發(fā)送。

      讀取服務(wù)器端數(shù)據(jù)

    • 分為兩種:一種是讀取針對(duì)ConnectRequest請(qǐng)求的響應(yīng),另一種就是其他響應(yīng),先暫時(shí)不說。

      先來看看針對(duì)ConnectRequest請(qǐng)求的響應(yīng):

      如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

      首先進(jìn)行反序列化,得到ConnectResponse對(duì)象,我們就可以獲取到服務(wù)器端給我們客戶端分配的sessionId和passwd,以及協(xié)商后的sessionTimeOut時(shí)間。

    • 首選要根據(jù)協(xié)商后的sessionTimeout時(shí)間,重新計(jì)算readTimeout和connectTimeout值。然后保留和記錄sessionId和passwd。最后通過EventThread發(fā)送一個(gè)SyncConnected連接成功事件。至此,TCP連接和session初始化請(qǐng)求都完成了,客戶端的ZooKeeper對(duì)象可以正常使用了。

      至此,我們便了解客戶端與服務(wù)器端建立連接的過程。

4 服務(wù)器端處理連接的過程

服務(wù)器端情況分很多種,先暫時(shí)說最簡單的單機(jī)版。同時(shí)也不再給出服務(wù)器端的啟動(dòng)過程(后面的文章再來詳細(xì)說明)。

首先介紹下服務(wù)器端的大體概況:

  • 首先是服務(wù)器端的配置文件,有tickTime、minSessionTimeout、maxSessionTimeout相關(guān)屬性。默認(rèn)情況下,tickTime是3000ms,minSessionTimeout是2倍的tickTime,maxSessionTimeout是20倍的tickTime。

  • 服務(wù)器端默認(rèn)采用NIOServerCnxnFactory來負(fù)責(zé)socket的處理。每來一個(gè)客戶端socket請(qǐng)求,為該客戶端創(chuàng)建一個(gè)NIOServerCnxn。之后與該客戶端的交互,就交給了NIOServerCnxn來處理。對(duì)于客戶端的ConnectRequest請(qǐng)求,處理如下:

    首先反序列化出ConnectRequest

    如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

    然后開始協(xié)商sessionTimeout時(shí)間

即判斷用戶傳遞過來的sessionTimeout時(shí)間是否在minSessionTimeout、maxSessionTimeout之間。協(xié)商完成之后,根據(jù)用戶傳遞過來的sessionId是否是0進(jìn)行不同的處理。客戶端第一次請(qǐng)求,sessionId為0。當(dāng)客戶端已經(jīng)連接過一個(gè)服務(wù)器地址,分配了sessionId,然后如果發(fā)生超時(shí)等異常,客戶端會(huì)去拿著已經(jīng)分配的sessionId去連接下一個(gè)服務(wù)器地址,此時(shí)的sessionId不為0。

sessionId為0,則代表著要?jiǎng)?chuàng)建session。sessionId不為0,則需要對(duì)該sessionId進(jìn)行合法性檢查,以及是否已經(jīng)過期了的檢查。

我們先來看看sessionId為0的情況:

![創(chuàng)建session](https://static.oschina.net/uploads/img/201508/01065436_4nHs.png "創(chuàng)建session")

大體上分三大步:1、使用sessionTracker根據(jù)sessionTimeout時(shí)間創(chuàng)建一個(gè)新的session 2、根據(jù)sessionId創(chuàng)建出密碼
3、提交這個(gè)創(chuàng)建session的請(qǐng)求到請(qǐng)求處理器鏈,最終將sessionId和密碼返回給客戶端

下面來分別詳細(xì)的說明這三個(gè)過程:

4.1 使用sessionTracker創(chuàng)建session

SessionTracker是用來創(chuàng)建刪除session,執(zhí)行session的過期檢查的。

直接看下默認(rèn)使用的SessionTrackerImpl:

如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

先看下session有哪些屬性:

  • final long sessionId:session的唯一標(biāo)示

  • final int timeout:這個(gè)session的timeout時(shí)間(即上文中客戶端和服務(wù)器端商定下來的timeout時(shí)間)

  • long tickTime:這個(gè)session的下一次超時(shí)時(shí)間點(diǎn)(隨著客戶端不斷的發(fā)送PING請(qǐng)求,就會(huì)不斷的刷新該時(shí)間,不斷的往后變化)

  • boolean isClosing:session的標(biāo)示符,用于標(biāo)示session是否還被正常使用

  • Object owner:創(chuàng)建該session的owner。會(huì)在客戶端更換所連接的服務(wù)器的時(shí)候用到(之后詳細(xì)說明)

然后再來看看SessionTracker的幾個(gè)數(shù)據(jù):

  • HashMap<Long, SessionImpl> sessionsById:很簡單,以sessionId存儲(chǔ)session

  • ConcurrentHashMap<Long, Integer> sessionsWithTimeout:以sessionId存儲(chǔ)每個(gè)session的timeout時(shí)間

  • HashMap<Long, SessionSet> sessionSets:某個(gè)時(shí)間點(diǎn)上的session集合(用于session過期檢查)

  • long nextSessionId:初始的sessionId,之后創(chuàng)建的sessionId就在此基礎(chǔ)上自增

  • nextExpirationTime:下一次過期時(shí)間點(diǎn),每當(dāng)?shù)皆摃r(shí)間點(diǎn)就會(huì)進(jìn)行一次session的過期檢查

  • expirationInterval:session過期檢查的周期

要搞清楚的內(nèi)容有:1 創(chuàng)建session的過程 2 session過期檢查的過程

先來看看創(chuàng)建session的過程:代碼很簡單,就是創(chuàng)建一個(gè)SessionImpl對(duì)象,然后存儲(chǔ)到SessionTracker中,同時(shí)開始計(jì)算session的超時(shí)時(shí)間。這里有一個(gè)內(nèi)容就是sessionId的來歷,我們可以看到就是根據(jù)nextSessionId來的,并且是不斷自增的。

sessionId是一個(gè)客戶端的重要標(biāo)示,是全局唯一的,先來看看單機(jī)版的nextSessionId初始化:

如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

單機(jī)版的服務(wù)器使用1通過計(jì)算來初始化nextSessionId。而集群版對(duì)應(yīng)的id則分別是每個(gè)機(jī)器指定的sid。

  • 第一步:就是取當(dāng)前時(shí)間,為 10100111011100110110010101110100111100011 為41為二進(jìn)制

  • 第二步:long有64位,左移24位,其實(shí)是除掉了前面的1,后面補(bǔ)了24位的0。

  • 第三步:第二步的結(jié)果可能是正數(shù)也可能是負(fù)數(shù),目前是正數(shù),之后可能就是負(fù)數(shù)了,你可以算一下需要多少年,哈哈。為了保證右移的時(shí)候,進(jìn)行補(bǔ)0操作,需要使用無符號(hào)右移,即>>>。這里使用了無符號(hào)右移8位

  • 第四步:將傳過來的id這里即1左移56位。然后再與第三步的正數(shù)結(jié)果進(jìn)行或操作,得到最終的基準(zhǔn)nextSessionId,所以當(dāng)這里的id值不是很大的話,一般幾臺(tái)機(jī)器而已,也保證了sessionId是一個(gè)正數(shù),同時(shí)前八位就是機(jī)器的sid號(hào)。所以每臺(tái)機(jī)器的的前八位是不同的,保證了每臺(tái)機(jī)器中不會(huì)配置相同的sessionId,每臺(tái)機(jī)器的sessionId又是自增操作,所以單臺(tái)機(jī)器內(nèi)sessionId也是不會(huì)重復(fù)的。

綜上所示保證了sessionId是唯一的,不會(huì)出現(xiàn)重復(fù)分配的情況。

搞清楚了sessionId的分配,接下來就要弄清楚如何進(jìn)行session的過期檢查問題:

我們先看下,session激活過程是怎么處理的:

如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

  • 首先獲取這個(gè)session數(shù)據(jù),然后計(jì)算它的超期時(shí)間

    long expireTime = roundToInterval(System.currentTimeMillis() + timeout);

    private long roundToInterval(long time) {

       // We give a one interval grace period
       return (time / expirationInterval + 1) * expirationInterval;


    }

    即是拿當(dāng)前時(shí)間加上這個(gè)session的timeout時(shí)間,然后對(duì)其進(jìn)行取expirationInterval的整,即始終保持是expirationInterval的正數(shù)倍,即每個(gè)session的過期時(shí)間點(diǎn)最終都會(huì)落在expirationInterval的整數(shù)倍上。

  • 如果原本該session的超期時(shí)間就大于你所計(jì)算出的超期時(shí)間,則不做任何處理,否則設(shè)置該session的超期時(shí)間為上述計(jì)算結(jié)果的超期時(shí)間。

  • 取出原本該session所在的超期時(shí)間,從集合里面刪除

  • 重新獲取現(xiàn)在超期時(shí)間所在的集合,添加進(jìn)去

綜上所述,session的激活其實(shí)就是重新計(jì)算下超時(shí)時(shí)間,最終取expirationInterval的正數(shù)倍,然后從之前時(shí)間點(diǎn)的集合中移除,然后再添加到新的時(shí)間點(diǎn)的集合中去。

至此,session的檢查就方便多了,只需要在expirationInterval整數(shù)時(shí)間點(diǎn)上取出集合,然后一個(gè)個(gè)標(biāo)記為過期即可。而那些不斷被激活的session,則不斷的從一個(gè)時(shí)間點(diǎn)的集合中換到下一個(gè)時(shí)間點(diǎn)的集合中。

SessionTrackerImpl也是一個(gè)線程,該線程執(zhí)行內(nèi)容就是session的過期檢查。

4.2 根據(jù)sessionId創(chuàng)建出密碼

回到創(chuàng)建session的三大步驟:

如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

來看下密碼是如何來產(chǎn)生的:

Random r = new Random(sessionId ^ superSecret);
r.nextBytes(passwd);

其中superSecret為常量

static final private long superSecret = 0XB3415C00L;

使用Random的方式來隨機(jī)生成字節(jié)數(shù)組。但是該字節(jié)數(shù)組,只要參數(shù)即sessionId相同,字節(jié)數(shù)組的內(nèi)容就相同。即當(dāng)我們知道了sessionId,就可以利用上述方式算出對(duì)應(yīng)的密碼,我感覺密碼基本上沒什么用。

再看下當(dāng)客戶端帶著sessionId和密碼進(jìn)行連接的時(shí)候,這時(shí)會(huì)進(jìn)行密碼的檢查

看了上面的代碼,就再次驗(yàn)證了密碼沒什么鳥用,知道了sessionId,就完全知道了密碼。所以這一塊有待改進(jìn)吧,應(yīng)該不能由sessionId完全決定吧,如再加上當(dāng)前時(shí)間等等,讓客戶端造不出來密碼,同時(shí)服務(wù)器端存儲(chǔ)加密后的密碼。

4.2 提交這個(gè)創(chuàng)建session的請(qǐng)求到請(qǐng)求處理器鏈

本文內(nèi)容已太多,這里就先簡單描述下,之后再詳細(xì)的講解

如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析

如果是成功創(chuàng)建session,則把sessionTimeout、sessionId、passwd傳遞給客戶端。如果沒有成功創(chuàng)建,上述三者的值分別是0,0,new byte[16]

之后客戶端處理該響應(yīng)的過程,上面已經(jīng)說了,可以回頭再看下。

上述就是小編為大家分享的如何進(jìn)行ZooKeeper中的客戶端創(chuàng)建連接過程分析了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI