【ZooKeeper Notes 12】ZooKeeper 會話超時

轉載請註明:@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

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

一種場景是Server服務器掛了,這個時候,ZK客戶端首選會捕獲異常,以下:socket

捕獲異常後,ZK客戶端會打印相似於以下日誌:ide

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

而後作一些socket鏈接的善後工做。接下去是客戶端從新選擇一個Server Ip嘗試鏈接,邏輯如代碼C2-1所示,這裏主要就是從地址列表中獲取一個新的Server地址進行鏈接,關於這個地址如何獲取,請查看這個文章:《ZooKeeper客戶端地址列表的隨機原理》。this

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

 程序運行過程當中,整個過程日誌打印大體以下:編碼

  
  
           
  
  
  1. 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 
  2.  
  3. 收到事件通知:Disconnected 
  4.  
  5. 獲取數據成功,path:/nileader 
  6. 2012-10-31 09:09:58,293 - INFO  [main-SendThread-zookeeper.ClientCnxn$SendThread@933] - Opening socket connection to server /1.2.1.1:2181 
  7.  
  8. 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. 
  9.  
  10. 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. 
  11.  
  12. 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 
  13.  
  14. 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 
  15.  
  16. 收到事件通知:SyncConnected 

 因此,如今對於「鏈接斷開」這個過程就一目瞭然了,核心流程以下:spa

ZK客戶端捕獲「鏈接斷開」異常 ——> 獲取一個新的ZK地址 ——> 嘗試鏈接

在這個流程中,咱們能夠發現,整個過程不須要開發者額外的程序介入,都是ZK客戶端本身會進行的,而且,使用的會話ID都是同一個,因此結論就是:發生CONNECTIONLOSS的狀況,應用不須要作什麼事情,等待ZK客戶端創建新的鏈接便可。

 三、會話超時
SESSIONEXPIRED發生在上面藍色文字部分,這個一般是ZK客戶端與服務器的鏈接斷了,試圖鏈接上新的ZK機器,可是這個過程若是耗時過長,超過了SESSION_TIMEOUT 後尚未成功鏈接上服務器,那麼服務器認爲這個Session已經結束了(服務器沒法確認是由於其它異常緣由仍是客戶端主動結束會話),因爲在ZK中,不少數據和狀態都是和會話綁定的,一旦會話失效,那麼ZK就開始清除和這個會話有關的信息,包括這個會話建立的臨時節點和註冊的全部Watcher。在這以後,因爲網絡恢復後,客戶端可能會從新鏈接上服務器,可是很不幸,服務器會告訴客戶端一個異常:SESSIONEXPIRED(會話過時)。此時客戶端的狀態變成 CLOSED狀態,應用要作的事情就是的看本身應用的複雜程序了,要從新實例zookeeper對象,而後從新操做全部臨時數據(包括臨時節點和註冊Watcher),總之,會話超時在ZK使用過程當中是真實存在的。
 
因此這裏也簡單總結下,一旦發生會話超時,那麼存儲在ZK上的全部臨時數據與註冊的訂閱者都會被移除,此時須要從新建立一個ZooKeeper客戶端實例,須要本身編碼作一些額外的處理。
 
四、會話時間(Session Time)
在《 ZooKeeper API 使用》一文中已經提到,在實例化一個ZK客戶端的時候,須要設置一個會話的超時時間。這裏須要注意的一點是,客戶端並非能夠隨意設置這個會話超時時間,在ZK服務器端對會話超時時間是有限制的,主要是 minSessionTimeoutmaxSessionTimeout這兩個參數設置的。(詳細查看這個文章《 ZooKeeper管理員指南》)Session超時時間限制,若是客戶端設置的超時時間不在這個範圍,那麼會被強制設置爲最大或最小時間。 默認的Session超時時間是在2 * tickTime ~ 20 * tickTime。ZK實現以下,在這個方法中處理:
  
  
           
  
  
  1. ZooKeeperServer.processConnectRequest(ServerCnxn cnxn, ByteBuffer incomingBuffer) 

因此,若是應用對於這個會話超時時間有特殊的需求的話,必定要和ZK管理員溝通好,確認好服務端是否設置了對會話時間的限制。 

相關文章
相關標籤/搜索