摘要:前言本文為篤行日常工作記錄,爛筆頭系列。最終通過分析源碼了解到最終的確定是一個協商的過程,而不是簡單的配置生效。根據客戶端上報的和服務端自身的。如果上報的小于則設置為如果上報的大于則設置為如果介于兩則之間,則以上報的時間為準。
0.前言
本文為篤行日常工作記錄,爛筆頭系列。
源碼前面,了無秘密 — by 侯杰
近期的一個C++項目里使用了Zookeeper做服務發現,期間遇到了SessionTimeOut問題的困擾,明明通過zookeeper c client設置了超時時間,但無效。
請原諒我一開始對zookeeper不熟悉。最終通過分析源碼了解到SessionTimeOut最終的確定是一個協商的過程,而不是簡單的配置生效。
在這里記錄下Session超時時間的有關分析,基于zookeeper 3.4.8
1.zookeeper client SessionTimeOut項目中使用的是 C client,通過zookeer_init 創建zk session,調用了zookeeper_init其實就開始了建立鏈接到ZK集群的過程,這里設置的recv_timeout 為客戶端所期望的session超時時間,單位為毫秒。
ZOOAPI zhandle_t *zookeeper_init(const char *host, watcher_fn fn, int recv_timeout, const clientid_t *clientid, void *context, int flags);
連接成功之后客戶端發起握手協議,可以看到之前設置的recv_timeout隨握手協議一起發送給服務端,zookeeper.c#L1485。
static int prime_connection(zhandle_t *zh) { int rc; /*this is the size of buffer to serialize req into*/ char buffer_req[HANDSHAKE_REQ_SIZE]; int len = sizeof(buffer_req); int hlen = 0; struct connect_req req; req.protocolVersion = 0; req.sessionId = zh->seen_rw_server_before ? zh->client_id.client_id : 0; req.passwd_len = sizeof(req.passwd); memcpy(req.passwd, zh->client_id.passwd, sizeof(zh->client_id.passwd)); req.timeOut = zh->recv_timeout; <-這里設置timeOut req.lastZxidSeen = zh->last_zxid; req.readOnly = zh->allow_read_only; hlen = htonl(len); /* We are running fast and loose here, but this string should fit in the initial buffer! */ rc=zookeeper_send(zh->fd, &hlen, sizeof(len)); serialize_prime_connect(&req, buffer_req); rc=rc<0 ? rc : zookeeper_send(zh->fd, buffer_req, len); if (rc<0) { return handle_socket_error_msg(zh, __LINE__, ZCONNECTIONLOSS, "failed to send a handshake packet: %s", strerror(errno)); }
再來看看處理握手協議Resp的邏輯 zookeeper.c L1767
static int check_events(zhandle_t *zh, int events) { if (zh->fd == -1) return ZINVALIDSTATE; …… …… …… deserialize_prime_response(&zh->primer_storage, zh->primer_buffer.buffer); /* We are processing the primer_buffer, so we need to finish * the connection handshake */ oldid = zh->client_id.client_id; newid = zh->primer_storage.sessionId; if (oldid != 0 && oldid != newid) { zh->state = ZOO_EXPIRED_SESSION_STATE; errno = ESTALE; return handle_socket_error_msg(zh,__LINE__,ZSESSIONEXPIRED, "sessionId=%#llx has expired.",oldid); } else { zh->recv_timeout = zh->primer_storage.timeOut; //設置為Resp的Timeout zh->client_id.client_id = newid; }
至此可以發現,最終客戶端的SessionTimeOut時間實際是經過服務端下發之后的,并不一定是最先設置的。
2.Zookeeper Server SessionTimeOut 2.1協商客戶端上報的SessionTimeOut來看看服務端握手的處理邏輯ZooKeeperServer.java#L876。
public void processConnectRequest(ServerCnxn cnxn, ByteBuffer incomingBuffer) throws IOException { BinaryInputArchive bia = BinaryInputArchive.getArchive(new ByteBufferInputStream(incomingBuffer)); ConnectRequest connReq = new ConnectRequest(); connReq.deserialize(bia, "connect"); …… …… …… //根據客戶端上報的timeout和服務端自身的minSessionTimeOut。 //如果上報的timeout小于minSessionTimeOut則 設置timeout為minSessionTimeOut. //如果上報的timeout大于maxSessionTimeOut則 設置timeout為maxSessionTimeOut. //如果介于兩則之間,則以上報的時間為準。 int sessionTimeout = connReq.getTimeOut(); byte passwd[] = connReq.getPasswd(); int minSessionTimeout = getMinSessionTimeout(); if (sessionTimeout < minSessionTimeout) { sessionTimeout = minSessionTimeout; } int maxSessionTimeout = getMaxSessionTimeout(); if (sessionTimeout > maxSessionTimeout) { sessionTimeout = maxSessionTimeout; } cnxn.setSessionTimeout(sessionTimeout); …… …… …… }
可以一句話概括,客戶端上報的期望timeout一定要在服務端設置的上下界之間,如果越過邊界,則以邊界為準。
2.2 服務端MinSessionTimeOut和MaxSessionTimeOut的確定繼續看ZooKeeperServer.java#L104和ZooKeeperServer.java#L791
public static final int DEFAULT_TICK_TIME = 3000; protected int tickTime = DEFAULT_TICK_TIME; /** value of -1 indicates unset, use default */ protected int minSessionTimeout = -1; /** value of -1 indicates unset, use default */ protected int maxSessionTimeout = -1; protected SessionTracker sessionTracker;
tickTime為3000毫秒,minSessionTimeOut和maxSessionTimeOut缺省值為-1
public int getTickTime() { return tickTime; } public void setTickTime(int tickTime) { LOG.info("tickTime set to " + tickTime); this.tickTime = tickTime; } public int getMinSessionTimeout() { return minSessionTimeout == -1 ? tickTime * 2 : minSessionTimeout; //如果minSessionTimeOut為缺省值這設置minSessionTimeOut為2倍tickTime } public void setMinSessionTimeout(int min) { LOG.info("minSessionTimeout set to " + min); this.minSessionTimeout = min; } public int getMaxSessionTimeout() { return maxSessionTimeout == -1 ? tickTime * 20 : maxSessionTimeout; //如果maxSessionTimeout為缺省值則設置maxSessionTimeout為20倍tickTime } public void setMaxSessionTimeout(int max) { LOG.info("maxSessionTimeout set to " + max); this.maxSessionTimeout = max; }
可以知道minSessionTimeOut和maxSessionTimeOut在缺省的時候則跟tickTime有關,分別為2和20倍tickTime,繼續分析。ZooKeeperServer.java#L160
public ZooKeeperServer(FileTxnSnapLog txnLogFactory, int tickTime, int minSessionTimeout, int maxSessionTimeout, DataTreeBuilder treeBuilder, ZKDatabase zkDb) { serverStats = new ServerStats(this); this.txnLogFactory = txnLogFactory; this.zkDb = zkDb; this.tickTime = tickTime; this.minSessionTimeout = minSessionTimeout; this.maxSessionTimeout = maxSessionTimeout; LOG.info("Created server with tickTime " + tickTime + " minSessionTimeout " + getMinSessionTimeout() + " maxSessionTimeout " + getMaxSessionTimeout() + " datadir " + txnLogFactory.getDataDir() + " snapdir " + txnLogFactory.getSnapDir()); }
tickTime、minSessionTimeOut、maxSessionTimeOut實際構造函數傳入,當然還有一個無參構造函數以及一些setter和getter可以設置這幾個參數。
繼續分析ZooKeeperServerMain.java#L94
public void runFromConfig(ServerConfig config) throws IOException { LOG.info("Starting server"); FileTxnSnapLog txnLog = null; try { // Note that this thread isn"t going to be doing anything else, // so rather than spawning another thread, we will just call // run() in this thread. // create a file logger url from the command line args ZooKeeperServer zkServer = new ZooKeeperServer(); txnLog = new FileTxnSnapLog(new File(config.dataLogDir), new File( config.dataDir)); zkServer.setTxnLogFactory(txnLog); zkServer.setTickTime(config.tickTime); //我們可以發現實際運行的幾個參數除了默認值以外,可以通過配置文件來配置生效。 zkServer.setMinSessionTimeout(config.minSessionTimeout); zkServer.setMaxSessionTimeout(config.maxSessionTimeout); cnxnFactory = ServerCnxnFactory.createFactory(); cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns()); cnxnFactory.startup(zkServer); cnxnFactory.join(); if (zkServer.isRunning()) { zkServer.shutdown(); } } catch (InterruptedException e) { // warn, but generally this is ok LOG.warn("Server interrupted", e); } finally { if (txnLog != null) { txnLog.close(); } } }
到此問題就明了了,我們可以通過配置來修改SessionTimeOut,默認配置文件只配置了tickTime,如下。
# The number of milliseconds of each tick tickTime=2000 # The number of ticks that the initial # synchronization phase can take initLimit=10 # The number of ticks that can pass between # sending a request and getting an acknowledgement syncLimit=5 # the directory where the snapshot is stored. # do not use /tmp for storage, /tmp here is just # example sakes. dataDir=/tmp/zookeeper # the port at which the clients will connect clientPort=2181 # the maximum number of client connections. # increase this if you need to handle more clients #maxClientCnxns=60 # # Be sure to read the maintenance section of the # administrator guide before turning on autopurge. # # http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance # # The number of snapshots to retain in dataDir #autopurge.snapRetainCount=3 # Purge task interval in hours # Set to "0" to disable auto purge feature #autopurge.purgeInterval=13.總結
經過源碼分析,得出SessionTimeOut的協商如下:
情況1: 配置文件配置了maxSessionTimeOut和minSessionTimeOut
最終SessionTimeOut,必須在minSessionTimeOut和maxSessionTimeOut區間里,如果跨越上下界,則以跨越的上屆或下界為準。
情況2:配置文件沒有配置maxSessionTimeOut和minSessionTimeOut
maxSessionTimeout沒配置則 maxSessionTimeOut設置為 20 * tickTime
minSessionTimeOut沒配置則 minSessionTimeOut設置為 2 * tickTime
也就是默認情況下, SessionTimeOut的合法范圍為 4秒~40秒,默認配置中tickTime為2秒。
如果tickTime也沒配置,那么tickTime缺省為3秒。
遇到問題從源碼分析一定是最好的,能使得理解更深入記憶更深刻。
最后 ending...如有不足請指點,亦可留言或聯系 fobcrackgp@163.com.
本文為篤行原創文章首發于大題小作,永久鏈接:篤行雜記之Zookeeper SessionTimeOut分析
https://www.ifobnn.com/zookeepersessiontimeout.html
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67253.html
摘要:每個都可以通過其路徑唯一標識,同時每個節點還可以存儲少量數據。監聽機制,監聽某個當該發生變化時,會回調該,但是這個是一次性的,下次需要監聽時還得再注冊一次。 前面的文章中 我用netty實現了一個簡單的一對一的RPC 11個類實現簡單java rpc 接下來的文章中 我將使用zookeeper作為rpc調用的分布式注冊中心 從而實現多對多(多個調用者,多個提供者)的rpc調用,負載均...
摘要:作為整個集群的主節點,負責響應所有對狀態變更的請求。選舉是最重要的技術之一,也是保障分布式數據一致性的關鍵所在。這是由于半數以上投票通過決定的。另外需要注意的是,和構成集群的法定人數,也就是說,只有他們才參與新的選舉響應的提議。 showImg(https://segmentfault.com/img/remote/1460000016733126); 前言 提到ZooKeeper,相...
摘要:作為整個集群的主節點,負責響應所有對狀態變更的請求。選舉是最重要的技術之一,也是保障分布式數據一致性的關鍵所在。這是由于半數以上投票通過決定的。另外需要注意的是,和構成集群的法定人數,也就是說,只有他們才參與新的選舉響應的提議。 showImg(https://segmentfault.com/img/remote/1460000016733126); 前言 提到ZooKeeper,相...
摘要:所以,雅虎的開發人員就試圖開發一個通用的無單點問題的分布式協調框架,以便讓開發人員將精力集中在處理業務邏輯上。在立項初期,考慮到之前內部很多項目都是使用動物的名字來命名的例如著名的項目雅虎的工程師希望給這個項目也取一個動物的名字。 前言 提到ZooKeeper,相信大家都不會陌生。Dubbo,Kafka,Hadoop等等項目里都能看到它的影子。但是你真的了解 ZooKeeper 嗎?如...
摘要:的設計目標是將那些復雜且容易出錯的分布式一致性服務封裝起來,構成一個高效可靠的原語集,并以一系列簡單易用的接口提供給用戶使用。具有不可分割性即原語的執行必須是連續的,在執行過程中不允許被中斷。 該文已加入開源文檔:JavaGuide(一份涵蓋大部分Java程序員所需要掌握的核心知識)。地址:https://github.com/Snailclimb... showImg(https:...
閱讀 1378·2021-11-24 09:38
閱讀 2086·2021-09-22 15:17
閱讀 2341·2021-09-04 16:41
閱讀 3452·2019-08-30 15:56
閱讀 3511·2019-08-29 17:19
閱讀 1939·2019-08-28 18:09
閱讀 1249·2019-08-26 13:35
閱讀 1711·2019-08-23 17:52