在網絡環境非常差的情況下,使用zookeeper集群往往會遇到連接expired了:
客戶端提示連接從ZOO_CONNECTION_STATE變為ZOO_EXPIRED_SEESION_STATE,然后應用失去與zookeeper集群的連接。
在使用過程中,我們一般會加大客戶端的rec_timeout值,例如設置為30s,但對是否發生expired沒有太大影響,仔細查看文檔發現要在服務器端設置minSessionTimeout。
那么什么情況下連接會過期?
當客戶端創建連接時,會隨機和一個zookeeper節點創建連接,並互發heartbeat;session的信息會同步到其他zookeeper節點上,比如session的id、watcher、臨時目錄等等信息。
當網絡抖動或者與他創建連接的zk節點掉線了,這個時候client與zk的的session將斷開,此時,client無需處理,zk的重試機制會自動跟其他存活的zk節點創建session:
1)如果連接在minSessionTimeout之內連接成功,那么新的連接將同步之前斷開連接的臨時數據和watcher,並同步狀態。
2)如果這個重連接過程超過了minSessionTimeout,那么該session就會被zk集群提出去即expired掉,會清除該session所有資源和數據,主要包括臨時數據和watcher。當使用zookeeper_init指定session的id去重新連接,也會報錯,返回一個空的句柄回來。
下面我們來仔細看看:
1)一般我們會設置連接超時時間,在客戶端設置,其API為:
ZOOAPI zhandle_t *zookeeper_init(const char *host, watcher_fn fn, int recv_timeout, const clientid_t * clientid, void *context, int flags); 功能: 創建一個句柄(handle)和一個響應(response)這個句柄的會話(session)。 參數: host:zookeeper主機列表,用逗號間隔。 fn:用於監視的回調函數。 clientid:客戶端嘗試重連的先前會話的ID,如果不需要重連先前的會話,則設置為 0。客戶端可以通過調用 zoo_client_id來訪問一個已經連接上的並且有效的會話ID,如果clientid對應的會話超時,或者由於某種原因 clientid變為無效了,那么zookeeper_init 將返回一個非法的 zhandle_t,通過 zhandle_t 的狀態可以獲知 zookeeper_init 調用失敗的原因。 (通常為 ZOO_EXPIRED_SESSION_STATE). 意思是如果該ID的連接是被expired了的話,重新連接也會失敗,服務器已經清理了相關的資源和信息。 context:暫時用不到,忽略。(TODO) flags:設置為0,zookeeper開發團隊保留以后使用。
大量,包括代碼里面的注釋上都沒有說recv_timeout的意思,按字面意思,肯定不是session_timeout,而是多長時間zk創建連接不成功的時間?
2)在服務器端zoo.conf中有相關設置:minSessionTimeout,最小的客戶端超時時間,默認值為2個ticktime,單位是毫秒:
minSessionTimeout 最小的客戶端session超時時間,默認值為2個tickTime,單位是毫秒 maxSessionTimeout 最大的客戶端session超時時間,默認值為20個tickTime,單位是毫秒
3)於是我們最終修改的zoo.conf文件為:
tickTime=1000 dataDir=/opt/zookeeper/zkdata dataLogDir=/opt/zookeeper/zklogs clientPort=2181 initLimit=5 syncLimit=2 minSessionTimeout=16000 maxSessionTimeout=30000 server.1=xxxx:2888:3888 server.2=xxxx:2888:3888 server.3=xxxx:2888:3888
注意,在僅配置了minSessionTimeout參數時,zk會啟動失敗,提示該參數超過了maxSessionTimeout值,這個時候需要在配置文件把最大值也配上。
4)服務端配置詳解:
(1)dataDir 用於存放內存數據庫快照的文件夾,同時用於集群的myid文件也存在這個文件夾里。 (2)dataLogDir 用於單獨設置transaction log的目錄,transaction log分離可以避免和普通log還有快照的競爭。 (3)tickTime 心跳時間,為了確保client-server連接存在的,以毫秒為單位,最小超時時間為兩個心跳時間。 (4)clientPort 客戶端監聽端口。 (5)globalOutstandingLimit client請求隊列的最大長度,防止內存溢出,默認值為1000。 (6)preAllocSize 預分配的Transaction log空間block為proAllocSize KB,默認block為64M,一般不需要更改,除非snapshot過於頻繁。 (7)snapCount 在snapCount個snapshot后寫一次transaction log,默認值是100,000。 (8)traceFile 用於記錄請求的log,打開會影響性能,用於debug,最好不要定義。 (9)maxClientCnxns 最大並發客戶端數,用於防止DOS的,默認值是10,設置為0是不加限制。 (11)clientPortBindAddress 可以設置指定的client ip以及端口,不設置的話等於ANY:clientPort (12)minSessionTimeout 最小的客戶端session超時時間,默認值為2個tickTime,單位是毫秒 (13)maxSessionTimeout 最大的客戶端session超時時間,默認值為20個tickTime,單位是毫秒 (14)electionAlg 用於選舉的實現的參數: ①0:為以原始的基於UDP的方式協作 ②1:為不進行用戶驗證的基於UDP的快速選舉 ③2:為進行用戶驗證的基於UDP的快速選舉 ④3:為基於TCP的快速選舉,默認值為3 (15)initLimit 多少個tickTime內,允許其他server連接並初始化數據,如果zooKeeper管理的數據較大,則應相應增大這個值。 (16)syncLimit 多少個tickTime內,允許follower同步,如果follower落后太多,則會被丟棄。 (17)leaderServes leader是否接受客戶端連接。默認值為yes。leader負責協調更新。當更新吞吐量遠高於讀取吞吐量時,可以設置為不接受客戶端連接,以便leader可以專注於同步協調工作。 (18)server.x=[hostname]:nnnnn[:nnnnn] 配置集群里面的主機信息,其中: ①server.x:server.x的x要寫在myid文件中,決定當前機器的id, ②第一個port用於連接leader, ③第二個用於leader選舉。 ④如果electionAlg為0,則不需要第二個port。 ⑤hostname也可以填ip。 (19)group.x=nnnnn[:nnnnn] 分組信息,表明哪個組有哪些節點,例如group.1=1:2:3 group.2=4:5:6 group.3=7:8:9。 (20)weight.x=nnnnn 權重信息,表明哪個結點的權重是多少,例如weight.1=1 weight.2=1 weight.3=1。