【轉】ZooKeeper 會話超時


原文鏈接 http://www.chepoo.com/zookeeper-session-timeout.html

 

1、會話概述

在ZooKeeper中,客戶端和服務端建立連接后,會話隨之建立,生成一個全局唯一的會話ID(Session ID)。

服務器和客戶端之間維持的是一個長連接,在SESSION_TIMEOUT時間內,服務器會確定客戶端是否

正常連接(客戶端會定時向服務器發送heart_beat,服務器重置下次SESSION_TIMEOUT時間)。

因此,在正常情況下,Session一直有效,並且ZK集群所有機器上都保存這個Session信息。

在出現網絡或其它問題情況下(例如客戶端所連接的那台ZK機器掛了,或是其它原因的網絡閃斷),

客戶端與當前連接的那台服務器之間連接斷了,這個時候客戶端會主動在

地址列表(實例化ZK對象的時候傳入構造方法的那個參數connectString)中選擇新的地址進行連接。

2、連接斷開

好了,上面基本就是服務器與客戶端之間維持會話的過程了。在這個過程中,

用戶可能會看到兩類異常CONNECTIONLOSS(連接斷開)和SESSIONEXPIRED(Session過期)。

連接斷開(CONNECTIONLOSS)一般發生在網絡的閃斷或是客戶端所連接的服務器掛機的時候,

這種情況下,ZooKeeper客戶端自己會首先感知到這個異常,具體邏輯是在如下方法中觸發的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void org.apache.zookeeper.ClientCnxn.SendThread.run(){ 
…… 
…… 
    } catch (Throwable e) { 
        if (closing) { 
            if (LOG.isDebugEnabled()) { 
                // closing so this is expected 
                LOG.debug("An exception was thrown while closing send thread for session 0x" 
                        + Long.toHexString(getSessionId()) 
                        + " : " + e.getMessage()); 
            } 
            break; 
        } else { 
            // this is ugly, you have a better way speak up 
            if (e instanceof SessionExpiredException) { 
                LOG.info(e.getMessage() + ", closing socket connection"); 
            } else if (e instanceof SessionTimeoutException) { 
                LOG.info(e.getMessage() + RETRY_CONN_MSG); 
            } else if (e instanceof EndOfStreamException) { 
                LOG.info(e.getMessage() + RETRY_CONN_MSG); 
            } else if (e instanceof RWServerFoundException) { 
                LOG.info(e.getMessage()); 
            } else { 
    ……                 
    ……                     
}

一種場景是Server服務器掛了,這個時候,ZK客戶端首選會捕獲異常,如下:
085824958
捕獲異常后,ZK客戶端會打印類似於如下日志:

1
EndOfStreamException: Unable to read additional data from server sessionid 0x13ab17ad9ec000b, likely server has closed socket

然后做一些socket連接的善后工作。接下去是客戶端重新選擇一個Server Ip嘗試連接,邏輯如代碼C2-1所示,

這里主要就是從地址列表中獲取一個新的Server地址進行連接.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[C2-1] 
private void startConnect() throws IOException { 
    state = States.CONNECTING; 
 
    InetSocketAddress addr; 
    if (rwServerAddress != null) { 
        addr = rwServerAddress; 
        rwServerAddress = null; 
    } else { 
        addr = hostProvider.next(1000); 
    } 
 
    LOG.info("Opening socket connection to server " + addr); 
 
    setName(getName().replaceAll("\\(.*\\)", 
            "(" + addr.getHostName() + ":" + addr.getPort() + ")")); 
    try { 
        zooKeeperSaslClient = new ZooKeeperSaslClient("zookeeper/"+addr.getHostName()); 
    } catch (LoginException e) { 
        LOG.warn("SASL authentication failed: " + e + " Will continue connection to Zookeeper server without " 
                + "SASL authentication, if Zookeeper server allows it."); 
        eventThread.queueEvent(new WatchedEvent( 
                Watcher.Event.EventType.None, 
                Watcher.Event.KeeperState.AuthFailed, null)); 
    } 
    clientCnxnSocket.connect(addr); 
}

090146268
程序運行過程中,整個過程日志打印大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2012-10-31 09:09:57,379 - INFO  [main-SendThread(test.zookeeper.connection_string2:2181):zookeeper.ClientCnxn$SendThread@1053] - Unable to read additional data from server sessionid 0x23ab45c87df0000, likely server has closed socket, closing socket connection and attempting reconnect 
 
收到事件通知:Disconnected 
 
獲取數據成功,path:/nileader 
2012-10-31 09:09:58,293 - INFO  [main-SendThread-zookeeper.ClientCnxn$SendThread@933] - Opening socket connection to server /1.2.1.1:2181 
 
2012-10-31 09:09:58,294 - WARN  [main-SendThread-client.ZooKeeperSaslClient@123] - SecurityException: java.lang.SecurityException: Unable to locate a login configuration occurred when trying to find JAAS configuration. 
 
2012-10-31 09:09:58,295 - INFO  [main-SendThread-client.ZooKeeperSaslClient@125] - Client will not SASL-authenticate because the default JAAS configuration section 'Client' could not be found. If you are not using SASL, you may ignore this. On the other hand, if you expected SASL to work, please fix your JAAS configuration. 
 
2012-10-31 09:09:58,296 - INFO  [main-SendThread-zookeeper.ClientCnxn$SendThread@846] - Socket connection established to test.zookeeper.connection_string/1.2.1.1:2181, initiating session 
 
2012-10-31 09:09:58,299 - INFO  [main-SendThread-zookeeper.ClientCnxn$SendThread@1175] - Session establishment complete on server test.zookeeper.connection_string/1.2.1.1:2181, sessionid = 0x23ab45c87df0000, negotiated timeout = 10000 
 
收到事件通知:SyncConnected

所以,現在對於“連接斷開”這個過程就一目了然了,核心流程如下:
ZK客戶端捕獲“連接斷開”異常 ——> 獲取一個新的ZK地址 ——> 嘗試連接
在這個流程中,我們可以發現,整個過程不需要開發者額外的程序介入,都是ZK客戶端自己會進行的,

並且,使用的會話ID都是同一個,所以結論就是:發生CONNECTIONLOSS的情況,

應用不需要做什么事情,等待ZK客戶端建立新的連接即可。
3、會話超時
SESSIONEXPIRED發生在上面藍色文字部分,這個通常是ZK客戶端與服務器的連接斷了,試圖連接上新的ZK機器,

但是這個過程如果耗時過長,超過了SESSION_TIMEOUT 后還沒有成功連接上服務器,

那么服務器認為這個Session已經結束了(服務器無法確認是因為其它異常原因還是客戶端主動結束會話),

由於在ZK中,很多數據和狀態都是和會話綁定的,一旦會話失效,那么ZK就開始清除和這個會話有關的信息,

包括這個會話創建的臨時節點和注冊的所有Watcher。在這之后,由於網絡恢復后,客戶端可能會重新連接上服務器,

但是很不幸,服務器會告訴客戶端一個異常:SESSIONEXPIRED(會話過期)。此時客戶端的狀態變成 CLOSED狀態,

應用要做的事情就是的看自己應用的復雜程序了,要重新實例zookeeper對象,

然后重新操作所有臨時數據(包括臨時節點和注冊Watcher),總之,會話超時在ZK使用過程中是真實存在的。

所以這里也簡單總結下,一旦發生會話超時,那么存儲在ZK上的所有臨時數據與注冊的訂閱者都會被移除,

此時需要重新創建一個ZooKeeper客戶端實例,需要自己編碼做一些額外的處理。

4、會話時間(Session Time)
在《ZooKeeper API 使用》一文中已經提到,在實例化一個ZK客戶端的時候,需要設置一個會話的超時時間。

這里需要注意的一點是,客戶端並不是可以隨意設置這個會話超時時間,在ZK服務器端對會話超時時間是有限制的,主

要是minSessionTimeout和maxSessionTimeout這兩個參數設置的。(詳細查看這個文章《ZooKeeper管理員指南》)Session超時時間限制,

如果客戶端設置的超時時間不在這個范圍,那么會被強制設置為最大或最小時間。

默認的Session超時時間是在2 * tickTime ~ 20 * tickTime。ZK實現如下,在這個方法中處理:
ZooKeeperServer.processConnectRequest(ServerCnxn cnxn, ByteBuffer incomingBuffer)
090801775
所以,如果應用對於這個會話超時時間有特殊的需求的話,一定要和ZK管理員溝通好,確認好服務端是否設置了對會話時間的限制。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM