本文作者:HelloGitHub-老荀
本文是 HelloZooKeeper 系列的最後一篇文章,接下來主要聊聊面試中如果被問到 ZooKeeper 的問題如何回答,也可以當作學完本系列的測試。
準備好了嗎?面試開始嘍~「首發於 HelloGitHub 公眾號」
一、模擬面試
終於來到重頭戲了,本小節我會從網上找到一些關於 ZK 的面試題進行剖析講解,並且站在面試官的基礎上分析考點,相信看完這一節,出去面試再碰到 ZK 相關的問題你便能披荊斬棘、所向披靡!
我先給大家模擬一個面試的場景:
面試官:我看你簡歷上用過 ZK,能給我介紹下嗎?你是怎麼理解 ZK 的作用呢?
(如果你把百度百科中的定義背給他聽,我只能說 666,千萬別這樣,會被別人當成傻子。)
我:我的理解 ZK 是一個脫離於應用的第三方程式,類似的資料庫,訊息佇列,Redis 等都是扮演這個角色,擁有一定的資料儲存和查詢能力,可以讓我們在現在都是分散式部署的應用之間“傳遞”資料,其次 ZK 支援的回撥通知,讓應用可以在一些業務場景中感知到資料的變化並及時作出相應的反應。最後,ZK 本身也支援叢集部署具有高可用的特點,是一個可靠的第三方中介軟體。
面試官:嗯,你剛剛提到了回撥通知,能仔細跟我聊聊 ZK 是怎麼去實現的嗎?
我:各種程式語言的客戶端都會對這個回撥通知進行抽象,通常需要開發者宣告一個 callback 的物件,在 Java 的客戶端中這個介面是 Watcher
,ZK 服務端提供了一些方法,比如 getData
、exists
或者最新版本中的 addWatch
都可以用來向 ZK 註冊回撥通知,而向服務端傳送的回撥通知,只會告訴服務端我當前的這個路徑需要被通知,服務端得知後,會在記憶體中記錄下來,路徑和客戶端之間的關係,客戶端自己也需要記錄下來,路徑和具體回撥的關係。當被訂閱的路徑發生事件的時候,各種增刪改吧,服務端就會從記憶體中的記錄去檢視有沒有需要通知的客戶端,有的話會傳送一個通知的請求給客戶端,客戶端收到通知後,就會從本地的記錄中取出對應的回撥物件去執行 callback 方法!
(實際情況,我覺得面試官可能不會讓你一直說下去,應該是互相聊的一個狀態)
面試官:嗯,說得挺詳細,那你剛剛提到的 getData
、exists
、 addWatch
三種註冊有什麼區別嗎?
我:getData
、exists
以及 getChildren
註冊的通知都是一次性的,當服務端通知過一次後,就會刪除記憶體中的記錄,之後如果仍然需要通知的話,客戶端就要去繼續註冊,而 addWatch
註冊的回撥通知是永久性的,只需要註冊一次可以一直被通知。
面試官:嗯好,你剛剛還提到了 ZK 有一定的資料儲存能力,你能說說 ZK 是怎麼儲存和整理資料的嗎?
我:ZK 的資料體現在兩部分。
面試官:哦?哪兩部分?
我:記憶體中和磁碟上。
面試官:那你先說說記憶體裡 ZK 是怎麼儲存資料
我:從邏輯上來講,ZK 記憶體中的資料其實是一個樹形結構,從 /
根節點開始,逐級向下用 /
分割,每一個節點下面還可以有多個子節點,就類似於 Unix 中的目錄結構,但在實際中,ZK 是使用一個 HashMap 去儲存整個樹形結構的資料的,key 是對應的全路徑字串,value 則是一個節點物件,包含了節點的各種資訊。
面試官:能說說你覺得為什麼要這麼設計嗎?
(其實我覺得一般面試官不會這麼問,以下回答也是我個人的猜想)
我:首先 HashMap 查詢速度很快,是 Java 標準庫中一個非常重要的資料結構,在許多地方都有用到。ZK 本身並不需要排序或者是範圍求值的操作,所以 HashMap 完全可以滿足查詢的需求。至於為什麼邏輯上要設計成樹形結構,父子節點,這個可能是因為這個結構和 Unix 檔案系統很像,非常便於理解以及基於路徑進行資料的分類,而且最新的 ZK 中有一些功能是依賴了父子遞迴這個特性的(比如 addWatch
),如果是普通的 key-value 是無法滿足的。
面試官:嗯好,那你再說說磁碟上 ZK 是怎麼儲存資料的呢?
我:ZK 在磁碟上規定了兩種檔案型別,一種是 log 檔案,一種是 snapshot。log 檔案是增量記錄,負責對每一個寫請求進行儲存,snapshot 檔案是全量記錄,是對記憶體的快照。
面試官:ZK 是怎麼保證記憶體中的資料和磁碟中的資料的一致性呢?
我:真正的強一致性,ZK 無法保證。對於每一次的寫請求,ZK 是採取先記錄磁碟再修改記憶體的,所以保證瞭如果出現意外的話,優先記錄磁碟可以儘可能的保證資料的完整。如果 ZK 是正常退出的話,也會強制刷磁碟檔案和生成 snapshot,保證了一致性,但如果是非正常退出的話,極端情況下的一部分資料是會丟失的。
面試官:你剛剛也提到了 ZK 本身也可以叢集部署的?能多聊一點嗎?
我:ZK 的配置檔案 zoo.cfg
中可以配置其他節點的資訊,各個節點通過 dataDir
目錄下的 myid
檔案進行區分,不同節點之間可以相互通訊,客戶端連上叢集中的任意一個節點都可以進行通訊。
面試官:ZK 叢集中有幾種不同的角色?你知道嗎?
我:有 Leader、Follower、Observer 三種角色。
面試官:說說他們之間的區別吧
我:叢集中有且只能有一個 Leader,Leader 負責對整個叢集的寫請求事務進行提交,在一個叢集選出 Leader 之前是無法對外提供服務的。Follower 和 Observer 都只能處理讀請求,區別是 Follower 有投票權可以參與 Leader 的競選,Observer 無法參與 Leader 的競選。
面試官:那你可以跟我講講,選舉 Leader 依靠哪些資訊嗎?
我:每一個節點都會維護三個最重要的資訊:epoch、zxid、myid。epoch 代表選舉的輪次,優先比較,如果相同則繼續比較下一級。zxid 代表本節點處理過的最大事務 ID,越大代表當前節點經手的寫請求越多,知道的也就越多,第二優先順序比較,如果還相同則比較 myid,myid 整個叢集中不能重複,所以最終一定能分出勝負。勝利的節點當選 Leader。
(準確的說,epoch 和 zxid 是一個欄位,一個記錄在高 32 位,一個記錄在低 32 位)
面試官:不同節點之間怎麼通訊呢?怎麼去進行選舉?
我:每一個 ZK 節點在啟動的時候,會通過讀取配置檔案中的叢集資訊,與其他節點建立 Socket 連線,叢集間的通訊就是通過這個 Socket。每個節點選舉的時候都把自己認為的候選人資訊廣播出去,同時也接收來自其他節點的候選人資訊,通過比較後,失敗的一方會更改自己的候選人資訊並重新進行廣播,反覆直到某一個節點得到半數以上投票,選舉就完成了。
面試官:不同的節點角色,在處理讀寫請求上有什麼不同嗎?你先聊聊 Leader 吧
我:好滴,Leader 作為叢集中的老大,負責對收到的寫請求發起提案 PROPOSAL,告訴其他節點當前收到一個寫請求,其他節點收到後,會在本地進行歸檔,其實就是寫入檔案輸出流,完畢後會傳送一個 ACK 給 Leader,Leader 統計到半數以上的 ACK 之後會再次傳送給其他節點一個 COMMIT,其他節點收到 COMMIT 之後就可以修改記憶體資料了。讀請求的話不需要提案直接查詢記憶體中的資料返回即可。
面試官:那 Follower 或 Observer 呢?
我:他們收到讀請求是一樣的,直接返回本地的記憶體資料即可。但是寫請求的話,會將當前請求轉發給 Leader,然後由 Leader 去處理,就和之前的流程是一樣的。
面試官:不同的請求 ZK 是如何保證順序呢?
我:這個順序的保證最終是落實在一個先進先出的佇列,優先進該佇列的請求會被先處理,所以能保證順序。
面試官:不同的客戶端的請求怎麼保證順序呢?A 先傳送了一個建立節點,在該請求返回之前,B 傳送了一個查詢該節點,B 會阻塞到 A 執行完畢再查詢嗎?還是直接返回查詢不到節點?
我:B 會直接返回查不到。不同的客戶端之間的順序 ZK 不保證,原因是在底層 ZK 是通過一個 Map 去分別放置不同的客戶端的請求的,不同的客戶端的 key 是不一樣的,而這個 Map 的 value 則是我剛剛提到的先進先出的佇列。所以只有同一個客戶端的請求能被順序執行,不同的客戶端是無法保證的。
面試官:能說說不同的客戶端的 key 是什麼嗎?怎麼保證不同。
我:每一個客戶端在連線至 ZK 後會被分配一個 sessionId,這個 sessionId 是通過當前時間戳、節點的 myid 和一個遞增特性生成的一個 long 型別欄位,可以保證不會重複。
面試官:說到 session,你知道 ZK 的會話是怎麼維持的嗎?
我:你問的是客戶端和服務端之間的會話嗎?
面試官:是的,你能跟我說說嗎?
我:每一個客戶端在連線 ZK 的時候會同時上報自己的超時時間,加上剛剛的 sessionId,ZK 的服務端會在本地維護一個對映關係,通過計算可以計算出該 sessionId 的超時時間,並且 ZK 自己也有一個 tickTime
的配置,通過一個演算法可以將不同客戶端不同超時間都對映到相同間隔的時間點上,再將這個超時時間和 sessionId 關係存起來。
面試官:對映到相同的時間點上有什麼好處嗎?
我:這樣服務端在啟動後,後臺會有一個執行緒,通過這個統一的時間間隔,取出 session 過期的客戶端,向他們傳送會話過期的訊息,極大的節約了效能。
面試官:客戶端是怎麼去更新會話的超時時間呢?
我:首先客戶端的每次操作都會重新整理這個超時時間,其次客戶端必須設計一個 PING 的操作,用於在客戶端空閒的時候主動去重新整理會話超時時間,防止過期。
面試官:除了客戶端和服務端之間的會話,還有別的嗎?
我:服務端和服務端之間也有心跳,而且服務端的心跳是由 Leader 主動發起的,向其他節點傳送 PING 請求,而其他節點收到 PING 後,需要把本地的會話資訊一併傳送給 Leader。
編不下去了,上面一些題具有我強烈的主觀偏好性,我覺得如果面試官是個菜雞的話,這些問題大部分都問不出來,所以重點是不在於我怎麼回答,而是當你對背後的原理了然於胸時,自然是神擋殺神,佛擋殺佛。
我說說我認為比較重要的幾個特性:
- 回撥通知,ZK 其他原理可以不懂,但是怎麼用回撥是肯定要知道的。
- 選舉,ZK 最具特色的一個屬性,基本都會問一下。
- 持久化,說清楚兩種檔案的區別。
- 會話,會話的概念,以及怎麼維持。
最後通過一個模擬面試回答了一下我認為 ZK 中比較有特點的面試問題,如果大家對面試問題還有什麼疑問記得留言給我噢~必須給你們安排上!
二、網上真題
我大部分題目是網上直接搜的,網址在這裡 ZooKeeper面試題(2020最新版)但是過濾了一些太 low 的題目。
我上面說了 Leader 在接受到寫請求後,會發起提案,然後等待其他節點的 ACK,這個 ACK 是要求半數以上通過才能繼續下去的,所以能收到半數以上的 ACK 說明叢集中的一半以上都已經完成了本地磁碟的歸檔,自然是保證了主從之間的資料同步。
我之前的文章中有介紹現在 ZK 中有 7 種節點型別,關於新節點的原理我還沒來得及講,所以他如果這麼問了你可以很官方的回答他:
- 持久節點
- 持久順序節點
- 臨時節點
- 臨時順序節點
他一般後面會接著問兩者的區別,臨時節點會隨著客戶端的會話斷開而自動刪除,原理就是在建立臨時節點的時候,服務端會維護一個 sessionId 和它對應的臨時節點路徑列表,當關閉會話時,把這個列表裡的路徑都拿出來一一刪除即可。而順序節點的區別就在於 ZK 會自動為路徑加上數字的字尾,僅此而已。
併發建立時,順序節點怎麼保證字尾數字唯一呢?
ZK 的請求是放入佇列裡一個個處理的,所以其實並沒有所謂的併發,前一個請求處理完再處理下一個請求,自然就能保證字尾數字的唯一性了。
ZK 將許可權分為兩大類,兩大類又能繼續細分:
- 客戶端的角色許可權
- IP
- 使用者名稱密碼
- world,最寬泛的許可權,也就是沒有許可權
- super,特殊的使用者名稱密碼,相當於管理員許可權
- 節點的資料許可權
- Create,建立
- Delete,刪除
- Read,讀
- Write,寫
- ACL,讀寫許可權
chroot 是 ZK 設計給客戶端的名稱空間隔離,作為不同客戶端的根節點,由客戶端去維護,總的來說就是傳送請求之前把 chroot 的路徑拼接上,再去請求服務端。chroot 對於服務端是透明的,完全不知道的。
Learner 和 Leader 之間同步資料是一個比較漫長和複雜的過程,總的來說可以大致分為以下步驟:
- Learner 上報自己的資訊給 Leader
- Leader 根據 Learner 資訊決定使用何種同步方法
- DIFF,直接從最近的 500 個提案中恢復資料,直接傳送提案即可
- TRUNC,通常出現於 Learner 是前 Leader,需要降級自己的資料達到和 Leader 一致
- SNAP,Leader 直接傳送整個記憶體快照給 Follower
- Leader 和 Learner 開始同步
- 同步完成後開始對外提供服務
三、配置大全
託大家的福,我把 ZK 的原始碼全部(爆肝)瀏覽了一遍,找到了至少 99% 的配置選項,ZK 的配置大致可以分為 3 種:
- 啟動命令列傳入的引數
zoo.cfg
配置檔案中的引數- 當前環境變數中的引數
3.1 命令列引數
命令列引數很少,而且沒有對應的配置名稱,這裡我簡單介紹下:
單機版只支援兩種形式的命令列傳參
- 客戶端監聽埠加 data 目錄,上一節原始碼除錯中用的就是這一個形式,例如:
2181 /your/zk/data/path
- 或者只傳一個引數,
zoo.cfg
的路徑,例如:/your/zoocfg/path
叢集版更簡單隻支援 zoo.cfg
的路徑一個引數
3.2 zoo.cfg 檔案中的配置
我仔細檢視原始碼的時候發現有些配置實際作用時需要計算又或者是一魚兩吃,被多個地方使用,所以很難一步到位的講清楚,所以下面的介紹僅供參考,配置項加星號(*)的是我未來打算開篇講解的。
配置項 | 預設值(單位) | 介紹 |
---|---|---|
dataDir | /tmp/zookeeper | 存放 snapshot、myid 檔案路徑 |
clientPort | 2181 | 監聽客戶端請求埠 |
tickTime | 2000(毫秒) | 影響客戶端會話檢查間隔、服務端之間心跳間隔 |
syncLimit | 5 | tickTime * syncLimit 決定了服務端心跳超時時間 |
initLimit | 10 | tickTime * initLimit 決定了 ACK 的超時時間 |
dataLogDir | 和 dataDir 一致 | 存放 log 檔案路徑 |
minSessionTimeout | tickTime * 2 | 客戶端的超時時間最小值 |
maxSessionTimeout | tickTime * 20 | 客戶端的超時時間最大值 |
electionAlg | 3 | 選舉演算法(1,2 已被廢棄) |
localSessionsEnabled* | false | 啟用本地會話 |
localSessionsUpgradingEnabled* | false | 本地會話可以升級成全域性會話 |
clientPortAddress | - | 客戶端的 host 要求,不配置的話可以接受任意發向 2181 的請求 |
secureClientPort | - | SSL 安全埠號 |
secureClientPortAddress | - | SSL 安全 host |
observerMasterPort* | - | 使 Observer 通過 Follower 去了解叢集中的選舉情況 |
clientPortListenBacklog | 50 | TCP 服務端用於臨時存放已完成三次握手的請求的佇列的最大長度 |
maxClientCnxns | 60 | 客戶端最大連線數 |
connectToLearnerMasterLimit | 0 | 決定了 Follower 連線 Leader 的超時時間 |
quorumListenOnAllIPs | false | 服務端是否接受來自任意 IP 地址的請求 |
peerType | - | 選項 observer / participant,決定節點角色 |
syncEnabled | true | Learner 是否需要本地持久化檔案 |
dynamicConfigFile* | - | 動態配置路徑 |
autopurge.snapRetainCount | 3 | 保留多少個最新的 snapshot 檔案 |
autopurge.purgeInterval | 0(小時) | 間隔多久進行一次 snapshot 的清理 |
standaloneEnabled | true | 是否允許單機模式啟動 |
reconfigEnabled* | false | 是否允許動態配置 |
sslQuorum | false | 叢集間是否使用 SSL 通訊 |
portUnification | false | 是否允許不安全連線 |
sslQuorumReloadCertFiles | false | 啟用金鑰更新時自動載入 |
quorum.auth.enableSasl | false | 啟用叢集間 SASL 鑑權 |
quorum.auth.serverRequireSasl | false | |
quorum.auth.learnerRequireSasl | false | |
quorum.auth.learner.saslLoginContext | QuorumLearner | |
quorum.auth.server.saslLoginContext | QuorumServer | |
quorum.auth.kerberos.servicePrincipal | zkquorum/localhost | |
quorum.cnxn.threads.size | 20 | 叢集間非同步建立連線執行緒池最大執行緒數 |
jvm.pause.info-threshold.ms | 1000(毫秒) | INFO 輸出暫停統計閾值 |
jvm.pause.warn-threshold.ms | 10000(毫秒) | WARN 輸出暫停統計閾值 |
jvm.pause.sleep.time.ms | 500(毫秒) | JVM 暫停統計執行緒 sleep 間隔 |
jvm.pause.monitor* | false | 是否啟用 JVM 暫停統計 |
metricsProvider.className* | DefaultMetricsProvider(全路徑) | 統計實現類路徑 |
multiAddress.enabled | false | |
multiAddress.reachabilityCheckTimeoutMs | 1000(毫秒) | |
multiAddress.reachabilityCheckEnabled | true | |
(以開頭)server.* | 叢集配置 | |
(以開頭)group* | 分組配置 | |
(以開頭)weight* | 權重 | |
(以開頭)metricsProvider.* | 自定義的統計配置 |
以上就是 3.6.2 中 zoo.cfg
所有的官方配置選項了
3.3 環境變數配置
Java 程式想要指定環境變數有兩種方法:
- 只需要在啟動的時候在後面加上
-DpropertyKey=propertyValue
即可 - ZK 還支援一種簡單的方式就是在
zoo.cfg
中直接指定(指定時不需要寫zookeeper.
的字首)。只要不是在上面 2.2 中 ZK 自己定義的配置項裡,ZK 啟動的時候讀取這些配置會自動幫他們新增zookeeper.
字首並加入當前環境變數中
如果該配置是 follower.nodelay
,就只能用第一種方式新增環境變數了。
讓我們也來看看 ZK 自己定義了哪些環境變數配置吧
配置項 | 預設值 | 配置 |
---|---|---|
zookeeper.server.realm* | - | 客戶端配置 |
zookeeper.clientCnxnSocket | ClientCnxnSocketNIO(全路徑) | 客戶端配置,通訊的實現類 |
zookeeper.client.secure | true | 客戶端配置 |
zookeeper.request.timeout | 0 | 客戶端配置,非同步 API 超時時間 |
zookeeper.server.principal | - | 客戶端配置 |
zookeeper.sasl.client.username | zookeeper | 客戶端配置 |
zookeeper.sasl.client.canonicalize.hostname | true | 客戶端配置 |
zookeeper.disableAutoWatchReset | false | 客戶端配置,會話超時自動清空 watcher |
zookeeper.sasl.clientconfig | - | |
zookeeper.sasl.client | true | 啟用 SASL |
zookeeper.ssl(.quorum).authProvider | x509 | SSL 實現類,加 quorum 的是服務端的配置,下同 |
zookeeper.ssl(.quorum).protocol | TLSv1.2 | |
zookeeper.ssl(.quorum).enabledProtocols | - | |
zookeeper.ssl(.quorum).ciphersuites | 根據不同的 jvm 版本 | |
zookeeper.ssl(.quorum).keyStore.location | - | |
zookeeper.ssl(.quorum).keyStore.password | - | |
zookeeper.ssl(.quorum).keyStore.type | - | |
zookeeper.ssl(.quorum).trustStore.location | - | |
zookeeper.ssl(.quorum).trustStore.password | - | |
zookeeper.ssl(.quorum).trustStore.type | - | |
zookeeper.ssl(.quorum).context.supplier.class | - | |
zookeeper.ssl(.quorum).hostnameVerification | true | |
zookeeper.ssl(.quorum).crl | false | |
zookeeper.ssl(.quorum).ocsp | false | |
zookeeper.ssl(.quorum).clientAuth | - | |
zookeeper.ssl(.quorum).handshakeDetectionTimeoutMillis | 5000(毫秒) | |
zookeeper.kinit | /usr/bin/kinit | |
zookeeper.jmx.log4j.disable | false | 禁用 jmx log4j |
zookeeper.admin.enableServer* | true | 是否啟用 Admin Server |
zookeeper.admin.serverAddress* | 0.0.0.0 | |
zookeeper.admin.serverPort* | 8080 | |
zookeeper.admin.idleTimeout* | 30000 | |
zookeeper.admin.commandURL* | /commands | |
zookeeper.admin.httpVersion* | 11 | |
zookeeper.admin.portUnification | false | |
zookeeper.DigestAuthenticationProvider.superDigest | - | 管理員賬號密碼 |
zookeeper.ensembleAuthName | - | |
zookeeper.requireKerberosConfig | - | |
zookeeper.security.auth_to_local | DEFAULT | |
(以開頭)zookeeper.authProvider.* | - | 自定義 scheme 校驗規則 |
zookeeper.letAnySaslUserDoX | - | |
zookeeper.SASLAuthenticationProvider.superPassword | - | |
zookeeper.kerberos.removeHostFromPrincipal | - | |
zookeeper.kerberos.removeRealmFromPrincipal | - | |
zookeeper.X509AuthenticationProvider.superUser | - | |
zookeeper.4lw.commands.whitelist* | - | 四字命令白名單 |
zookeeper.preAllocSize | 65536 * 1024 | |
zookeeper.forceSync | yes | |
zookeeper.fsync.warningthresholdms | 1000(毫秒) | fsync 告警閾值 |
zookeeper.txnLogSizeLimitInKb | -1(KB) | log 檔案大小 |
zookeeper.datadir.autocreate | true | data 目錄自動建立 |
zookeeper.db.autocreate | true | |
zookeeper.snapshot.trust.empty | false | 不信任空的 snapshot 檔案 |
zookeeper.snapshot.compression.method | 空字串 | snapshot 檔案壓縮實現 |
zookeeper.commitProcessor.numWorkerThreads | CPU 核心數 | |
zookeeper.commitProcessor.shutdownTimeout | 5000(毫秒) | |
zookeeper.commitProcessor.maxReadBatchSize | -1 | |
zookeeper.commitProcessor.maxCommitBatchSize | 1 | |
zookeeper.fastleader.minNotificationInterval | 200(毫秒) | 收集選票超時時間(初始) |
zookeeper.fastleader.maxNotificationInterval | 60000(毫秒) | 收集選票超時時間(最大) |
zookeeper.leader.maxTimeToWaitForEpoch | -1 | |
zookeeper.leader.ackLoggingFrequency | 1000 | |
zookeeper.testingonly.initialZxid | - | 初始化 zxid,僅供測試! |
zookeeper.leaderConnectDelayDuringRetryMs | 100 | Leaner 連線 Leader 超時時間 |
follower.nodelay | true | 設定 TCP no delay |
zookeeper.forceSnapshotSync | false | Learner 強制使用 snapshot 和 Leader 進行同步 |
zookeeper.leader.maxConcurrentSnapSyncs | 10 | |
zookeeper.leader.maxConcurrentDiffSyncs | 100 | |
zookeeper.observer.reconnectDelayMs | 0(毫秒) | Observer 延遲重連至 Leader |
zookeeper.observer.election.DelayMs | 200(毫秒) | Observer 延遲開始選舉 |
zookeeper.observerMaster.sizeLimit | 32 * 1024 * 1024 | |
zookeeper.electionPortBindRetry | 3 | 選舉埠連線重試次數 |
zookeeper.tcpKeepAlive | false | Socket keep alive 設定 |
zookeeper.cnxTimeout | 5000(毫秒) | Socket 超時時間 |
zookeeper.multiAddress.enabled | false | |
zookeeper.multiAddress.reachabilityCheckTimeoutMs | 1000(毫秒) | |
zookeeper.multiAddress.reachabilityCheckEnabled | true | |
zookeeper.quorumCnxnTimeoutMs | -1 | |
zookeeper.observer.syncEnabled | true | Observer 是否需要本地歸檔 |
zookeeper.bitHashCacheSize | 10 | 點陣圖初始快取大小 |
zookeeper.messageTracker.BufferSize | 10 | |
zookeeper.messageTracker.Enabled | false | |
zookeeper.pathStats.slotCapacity | 60 | |
zookeeper.pathStats.slotDuration | 15 | |
zookeeper.pathStats.maxDepth | 6 | |
zookeeper.pathStats.sampleRate | 0.1 | |
zookeeper.pathStats.initialDelay | 5 | |
zookeeper.pathStats.delay | 5 | |
zookeeper.pathStats.topPathMax | 20 | |
zookeeper.pathStats.enabled | false | |
zookeeper.watcherCleanThreshold | 1000 | |
zookeeper.watcherCleanIntervalInSeconds | 600 | |
zookeeper.watcherCleanThreadsNum | 2 | |
zookeeper.maxInProcessingDeadWatchers | -1 | |
zookeeper.watchManagerName | WatchManager(全路徑) | |
zookeeper.connection_throttle_tokens | 0 | |
zookeeper.connection_throttle_fill_time | 1 | |
zookeeper.connection_throttle_fill_count | 1 | |
zookeeper.connection_throttle_freeze_time | -1 | |
zookeeper.connection_throttle_drop_increase | 0.02 | |
zookeeper.connection_throttle_drop_decrease | 0.002 | |
zookeeper.connection_throttle_decrease_ratio | 0 | |
zookeeper.connection_throttle_weight_enabled | false | |
zookeeper.connection_throttle_global_session_weight | 3 | |
zookeeper.connection_throttle_local_session_weight | 1 | |
zookeeper.connection_throttle_renew_session_weight | 2 | |
zookeeper.extendedTypesEnabled* | false | 是否啟用 TTL 節點型別 |
zookeeper.emulate353TTLNodes* | false | 是否相容 3.5.3 的 TTL |
zookeeper.client.portUnification | false | |
zookeeper.netty.server.outstandingHandshake.limit | -1 | |
zookeeper.netty.advancedFlowControl.enabled | false | |
zookeeper.nio.sessionlessCnxnTimeout | 10000(毫秒) | |
zookeeper.nio.numSelectorThreads | CPU 核心數 / 2 再開方 | |
zookeeper.nio.numWorkerThreads | CPU 核心數 * 2 | |
zookeeper.nio.directBufferBytes | 64 * 1024(位元組) | |
zookeeper.nio.shutdownTimeout | 5000(毫秒) | |
zookeeper.request_stale_connection_check | true | |
zookeeper.request_stale_latency_check | false | |
zookeeper.request_throttler.shutdownTimeout | 10000(毫秒) | |
zookeeper.request_throttle_max_requests | 0 | |
zookeeper.request_throttle_stall_time | 100 | |
zookeeper.request_throttle_drop_stale | true | |
zookeeper.serverCnxnFactory | NIOServerCnxnFactory(全路徑) | |
zookeeper.maxCnxns | 0 | |
zookeeper.snapshotSizeFactor | 0.33 | |
zookeeper.commitLogCount | 500 | |
zookeeper.sasl.serverconfig | Server | |
zookeeper.globalOutstandingLimit | 1000 | |
zookeeper.enableEagerACLCheck | false | |
zookeeper.skipACL | no | |
zookeeper.allowSaslFailedClients | false | |
zookeeper.sessionRequireClientSASLAuth | false | |
zookeeper.digest.enabled | true | |
zookeeper.closeSessionTxn.enabled | true | |
zookeeper.flushDelay | 0 | |
zookeeper.maxWriteQueuePollTime | zookeeper.flushDelay / 3 | |
zookeeper.maxBatchSize | 1000 | |
zookeeper.intBufferStartingSizeBytes | 1024 | |
zookeeper.maxResponseCacheSize | 400 | |
zookeeper.maxGetChildrenResponseCacheSize | 400 | |
zookeeper.snapCount | 100000 | |
zookeeper.snapSizeLimitInKb | 4194304(千位元組) | |
zookeeper.largeRequestMaxBytes | 100 * 1024 * 1024 | |
zookeeper.largeRequestThreshold | -1 | |
zookeeper.superUser | - | |
zookeeper.audit.enable | false | 是否啟用 audit 日誌 |
zookeeper.audit.impl.class | Log4jAuditLogger(全路徑) | audit 日誌功能實現類 |
ZK 的配置還是很多的,有些我這裡 TODO 了,以後有機會和大家詳細介紹下~而且相當一部分的配置 ZK 官方的文件中已經給出瞭解釋,可以檢視 ZK 3.6.2 配置文件。
我這裡還要吐槽下,ZK 中有些配置是用 true 或者 false,有些使用 yes 或者 no,明顯是兩個(波)人開發的,這種不應該做一個統一嗎?yes 或 no 真的很多餘...
四、系列結語
感謝你們能看到這裡,陪伴這個系列從開始到現在!這個專案從有想法立項到之後跟蛋蛋溝通,再到正式開始編寫,到最後我寫下這段結語,大概經歷了三個多月(你們看到的時候應該是更晚),現在回頭再看之前寫的東西,感慨頗深。
(截圖來自-B 站何同學的視訊)
如果以我自己的自控能力,這玩意自己搞,搞著搞著可能就涼了,在此感謝蛋蛋給予我的幫助和鼓勵。關於 ZK 我的確之前有研究過一段時間,但是以現在的眼光看,當時的研究其實也就是皮毛而已(可能現在也還是),很多東西是我這次整理時現學的,收穫非常多,最直觀的感受就是,我以後出去面試不會再害怕 ZK 相關的問題了。
感謝大家這 3 個月的陪伴,本系列終結嘍!如果還有什麼想學的開源框架和技術可以留言告訴我們,後續繼續為大家安排免費的乾貨教程。
最最後,來個大大的贊吧!
關注 HelloGitHub 公眾號 收到第一時間的更新。
還有更多開源專案的介紹和寶藏專案等待你的發掘。