轉載請註明:@ni掌櫃 nileader@gmail.comjava
一、會話概述apache
在ZooKeeper中,客戶端和服務端創建鏈接後,會話隨之創建,生成一個全局惟一的會話ID(Session ID)。服務器和客戶端之間維持的是一個長鏈接,在SESSION_TIMEOUT時間內,服務器會肯定客戶端是否正常鏈接(客戶端會定時向服務器發送heart_beat,服務器重置下次SESSION_TIMEOUT時間)。所以,在正常狀況下,Session一直有效,而且ZK集羣全部機器上都保存這個Session信息。在出現網絡或其它問題狀況下(例如客戶端所鏈接的那臺ZK機器掛了,或是其它緣由的網絡閃斷),客戶端與當前鏈接的那臺服務器之間鏈接斷了,這個時候客戶端會主動在地址列表(實例化ZK對象的時候傳入構造方法的那個參數connectString)中選擇新的地址進行鏈接。服務器
二、鏈接斷開網絡
好了,上面基本就是服務器與客戶端之間維持會話的過程了。在這個過程當中,用戶可能會看到兩類異常CONNECTIONLOSS(鏈接斷開)和SESSIONEXPIRED(Session過時)。鏈接斷開(CONNECTIONLOSS)通常發生在網絡的閃斷或是客戶端所鏈接的服務器掛機的時候,這種狀況下,ZooKeeper客戶端本身會首先感知到這個異常,具體邏輯是在以下方法中觸發的:session
- 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客戶端首選會捕獲異常,以下:socket
捕獲異常後,ZK客戶端會打印相似於以下日誌:ide
- EndOfStreamException: Unable to read additional data from server sessionid 0x13ab17ad9ec000b, likely server has closed socket
而後作一些socket鏈接的善後工做。接下去是客戶端從新選擇一個Server Ip嘗試鏈接,邏輯如代碼C2-1所示,這裏主要就是從地址列表中獲取一個新的Server地址進行鏈接,關於這個地址如何獲取,請查看這個文章:《ZooKeeper客戶端地址列表的隨機原理》。this
- [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);
- }
程序運行過程當中,整個過程日誌打印大體以下:編碼
- 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
因此,如今對於「鏈接斷開」這個過程就一目瞭然了,核心流程以下:spa
ZK客戶端捕獲「鏈接斷開」異常 ——> 獲取一個新的ZK地址 ——> 嘗試鏈接
在這個流程中,咱們能夠發現,整個過程不須要開發者額外的程序介入,都是ZK客戶端本身會進行的,而且,使用的會話ID都是同一個,因此結論就是:發生CONNECTIONLOSS的狀況,應用不須要作什麼事情,等待ZK客戶端創建新的鏈接便可。
- ZooKeeperServer.processConnectRequest(ServerCnxn cnxn, ByteBuffer incomingBuffer)
因此,若是應用對於這個會話超時時間有特殊的需求的話,必定要和ZK管理員溝通好,確認好服務端是否設置了對會話時間的限制。