ZooKeeper: 简介, 配置及运维指南
1. 概覽
ZooKeeper是一個(gè)供其它分布式應(yīng)用程序使用的軟件, 它為其它分布式應(yīng)用程序提供所謂的協(xié)調(diào)服務(wù). 所謂的協(xié)調(diào)服務(wù), 是指ZooKeeper的如下能力
上面四個(gè)功能可能現(xiàn)在不太好說(shuō)清, 但大致上目前你需要明白ZooKeeper就是為其它分布式應(yīng)用程序提供一些基礎(chǔ)功能的程序就好了. 我們以其中的配置管理為例. 假設(shè)你在寫一個(gè)可橫向擴(kuò)容的集群應(yīng)用程序, 在你的預(yù)想里, 你的應(yīng)用程序最終將部署到成千上萬(wàn)臺(tái)機(jī)器上去, 那么, 這成千上萬(wàn)臺(tái)機(jī)器上的成千上萬(wàn)個(gè)實(shí)例如何保持配置文件的一致性呢? 簡(jiǎn)單: 你的分布式應(yīng)用程序不要自己處理配置管理的問(wèn)題, 而是把配置管理交給ZooKeeper去做, 如果某個(gè)實(shí)例需要取配置文件, 去訪問(wèn)ZooKeeper就好了, ZooKeeper向你保證一致性.
并且用上面說(shuō)到的四種功能, 還能組合實(shí)現(xiàn)出其它高級(jí)玩法, 比如:
下面各個(gè)章節(jié)將詳細(xì)敘述相關(guān)內(nèi)容.
需要說(shuō)明的是, ZooKeeper本身的官方文檔雖然質(zhì)量上乘(Apache產(chǎn)品的一貫風(fēng)格), 但內(nèi)容上并不完整, 我的這篇譯文也有兩個(gè)缺陷: 一是可能錯(cuò)譯, 由于我本身對(duì)ZooKeeper的理解出現(xiàn)了偏差導(dǎo)致的. 二是可能有廢話, 我并不是照章一字一句翻譯的. 總之請(qǐng)作為參考來(lái)閱讀, 不要作為標(biāo)準(zhǔn)來(lái)閱讀.
1.1 簡(jiǎn)介
1.1.1 一個(gè)為分布式應(yīng)用程序提供服務(wù)的 分布式協(xié)調(diào)服務(wù)
ZooKeeper是:
ZooKeeper對(duì)外提供了一系列的原語(yǔ)(primitives), 其它分布式應(yīng)用程序利用這些原語(yǔ), 可以很方便的實(shí)現(xiàn)一些高級(jí)抽象功能, 比如: 同步, 配置管理維護(hù), 分組, 命名等. ZooKeeper的設(shè)計(jì)宗旨就有兩條:
ZooKeeper是用Java寫的, 其對(duì)外的API有Java版本的, 也有C的binding版.
1.1.2 ZooKeeper的設(shè)計(jì)原則
ZooKeeper是簡(jiǎn)單易用的
如果將使用ZooKeeper的分布式應(yīng)用程序看做是千千萬(wàn)萬(wàn)個(gè)獨(dú)立的進(jìn)程實(shí)例, ZooKeeper則是構(gòu)建了一個(gè)共享的, 層級(jí)式的名稱空間(shared hierarchal namespace)以供這千千萬(wàn)萬(wàn)個(gè)進(jìn)程實(shí)例去訪問(wèn). 這個(gè)所謂的層級(jí)式的名稱空間邏輯上和*nix中的文件系統(tǒng)很像. 名稱空間由數(shù)據(jù)寄存器(data register)構(gòu)成, 數(shù)據(jù)寄存器也被稱謂節(jié)點(diǎn)(node), 類比于文件系統(tǒng)的話, 節(jié)點(diǎn)相當(dāng)于文件系統(tǒng)中的文件, 或目錄. 不過(guò)與文件系統(tǒng)不同的是: 文件系統(tǒng)是在持久化存儲(chǔ)設(shè)備上抽象數(shù)據(jù)存儲(chǔ)的一層, 但ZooKeeper是把所有數(shù)據(jù)都放在內(nèi)存中的.
ZooKeeper是既快又穩(wěn)的
數(shù)據(jù)全扔在內(nèi)存里, 保證的ZooKeeper很快, 性能很好. ZooKeeper的設(shè)計(jì)實(shí)現(xiàn)中, 除了簡(jiǎn)單易用, 還把以下三點(diǎn)列進(jìn)了宗旨里:
ZooKeeper本身是分布式的
ZooKeeper本身是分布式的. 我們把ZooKeeper寄生的多臺(tái)機(jī)器集合稱為一個(gè)ensemble, 這個(gè)詞不是很好翻譯, 干脆不翻譯了, 這個(gè)單詞的意思和集群差不多.
在下圖中, 每個(gè)Server是一個(gè)ZooKeeper實(shí)例進(jìn)程, 每個(gè)Client是一個(gè)使用ZooKeeper服務(wù)的客戶端, 這個(gè)客戶端一般是另外一個(gè)分布式程序的一個(gè)實(shí)例. 一個(gè)有效的ZooKeeper服務(wù)是由多個(gè)Server構(gòu)成的(實(shí)際上并沒(méi)有限定每個(gè)Server獨(dú)占一臺(tái)機(jī)器). 每個(gè)ZooKeeper服務(wù)中的Server都必須知道其它所有Server的位置. 每個(gè)Server都在內(nèi)存中保存了一些數(shù)據(jù)(in-memory image of state), 并且在磁盤上保存著事務(wù)日志(transaction logs)與快照(snapshot). 只要整個(gè)ZooKeeper服務(wù)中的大多數(shù)Server正常工作著, 那么整個(gè)Server就能正常的向外提供服務(wù).
而對(duì)于ZooKeeper的使用者, 也就是圖中的Client來(lái)說(shuō), 也就是另外一個(gè)分布式應(yīng)用程序(中的一個(gè)實(shí)例), 要使用ZooKeeper的服務(wù), 只需要通過(guò)TCP連接到ZooKeeper服務(wù)中的一個(gè)Server上即可, 將這個(gè)連接置為長(zhǎng)連接, 由Client負(fù)責(zé)TCP連接的維護(hù), 并通過(guò)這個(gè)TCP連接向Server發(fā)送請(qǐng)求, 接收回應(yīng), 發(fā)送心跳, 獲取監(jiān)控事件等. 當(dāng)Server不幸掛掉的時(shí)候, Client需要自己來(lái)將連接切換到另外一個(gè)Server上去.
ZooKeeper是嚴(yán)格有序的
ZooKeeper對(duì)每次內(nèi)部變更都有一個(gè)版本戳一樣的機(jī)制, 這也就是簡(jiǎn)單的事務(wù)機(jī)制. 它保證了ZooKeeper整個(gè)服務(wù)對(duì)外提供的原語(yǔ)是原子性的, 并且是嚴(yán)格有序的.
ZooKeeper很快
當(dāng)讀寫比例超過(guò)10:1的時(shí)候, ZooKeeper的性能表現(xiàn)很好. 低于這個(gè)比例的話, 可能就不是很適合使用ZooKeeper了.
1.1.3 ZooKeeper中的數(shù)據(jù)模型與層級(jí)式名稱空間
ZooKeeper中的層級(jí)式名稱空間和文件系統(tǒng)很類似, 每個(gè)節(jié)點(diǎn)都有獨(dú)一無(wú)二的一個(gè)路徑, 和文件系統(tǒng)一樣, 如下圖所示:
長(zhǎng)期節(jié)點(diǎn)與短期節(jié)點(diǎn)
和文件系統(tǒng)不同, 每個(gè)ZooKeeper節(jié)點(diǎn)都有自己的數(shù)據(jù), 而文件系統(tǒng)中, 目錄是不存儲(chǔ)數(shù)據(jù)的. ZooKeeper中的節(jié)點(diǎn)也被簡(jiǎn)稱為znode.
znode本身除了數(shù)據(jù)之外, 還存儲(chǔ)著:
每次znode中的數(shù)據(jù)有變更, 版本號(hào)都會(huì)遞增, 并且, Client向znode獲取數(shù)據(jù)時(shí), 實(shí)際上不光獲取了數(shù)據(jù)本身, 還獲取了數(shù)據(jù)的版本號(hào).
znode中數(shù)據(jù)的讀寫是原子性的, 讀, 會(huì)讀到整個(gè)znode中的所有數(shù)據(jù), 寫, 會(huì)替換掉znode中的所有數(shù)據(jù). 每個(gè)znode還額外有一個(gè)ACL(訪問(wèn)控制表. Access Control List)來(lái)限定讀寫的.
普通的znode是持久性的, 這意味著只要ZooKeeper服務(wù)健在, 那么這個(gè)znode就存在著. 但有一種znode不是這樣的: 它隨著某個(gè)創(chuàng)建znode的會(huì)話的開(kāi)始, 被創(chuàng)建, 而一旦這個(gè)會(huì)話被撤除掉, 這個(gè)znode就會(huì)自動(dòng)被ZooKeeper刪除. 這個(gè)特性在某些場(chǎng)合特別有用.
監(jiān)控
ZooKeeper支持一個(gè)叫監(jiān)控(watches)的概念: Client可以對(duì)某個(gè)znode設(shè)置監(jiān)控, 當(dāng)這個(gè)znode有變更的時(shí)候, 就會(huì)產(chǎn)生監(jiān)控事件, 這個(gè)事件會(huì)由ZooKeeper通知至Client, 即是Client會(huì)收到來(lái)自Server的通知.
另外如果在監(jiān)控過(guò)程中, Client和Server之間的連接掛掉了, 那么Client會(huì)收到一個(gè)連接掛掉的通知.
1.1.4 ZooKeeper對(duì)使用者的承諾
1.1.5 ZooKeeper的API
| create | 在名稱空間樹(shù)中的指定位置創(chuàng)建一個(gè)節(jié)點(diǎn) |
| delete | 刪除一個(gè)指定節(jié)點(diǎn) |
| exists | 查詢指定節(jié)點(diǎn)是否存在 |
| get data | 獲取指定節(jié)點(diǎn)上的數(shù)據(jù) |
| set data | 向指定節(jié)點(diǎn)寫數(shù)據(jù) |
| get children | 獲取一個(gè)指定節(jié)點(diǎn)的所有子節(jié)點(diǎn) |
| sync | 等待數(shù)據(jù)傳播完成 |
1.1.5 ZooKeeper的實(shí)現(xiàn)
下圖是ZooKeeper的組件圖: ZooKeeper是由下面圖中的幾個(gè)組件(component)構(gòu)成的, 除了圖中的request processor(請(qǐng)求接收器)組件之外, ZooKeeper的每個(gè)Server都有著其它組件的單獨(dú)實(shí)例.
replicated database(數(shù)據(jù)庫(kù)), 這里的replicated是指, 這個(gè)數(shù)據(jù)庫(kù)在每個(gè)Server中都有一個(gè)獨(dú)立的實(shí)例. 并且每個(gè)Server中的數(shù)據(jù)庫(kù)都存儲(chǔ)著整個(gè)名稱空間樹(shù), 另外注意這個(gè)數(shù)據(jù)庫(kù)是內(nèi)存數(shù)據(jù)庫(kù). 每當(dāng)數(shù)據(jù)樹(shù)要更新的時(shí)候, 都會(huì)向磁盤寫入日志(為了故障恢復(fù)使用), 并且對(duì)數(shù)據(jù)樹(shù)的寫操作, 是先將數(shù)據(jù)序列化后寫入磁盤, 再寫入數(shù)據(jù)庫(kù)的.
每個(gè)ZooKeeper Server都對(duì)外向多個(gè)Client提供服務(wù), 而Client只能接一個(gè)Server來(lái)收發(fā)請(qǐng)求回應(yīng). 當(dāng)Client發(fā)出讀請(qǐng)求的時(shí)候, 數(shù)據(jù)是直接從本地?cái)?shù)據(jù)庫(kù)返回的, 而如果Client發(fā)送的是一個(gè)寫請(qǐng)求, 那就有點(diǎn)麻煩了, 這個(gè)寫請(qǐng)求的處理要經(jīng)過(guò)一個(gè)協(xié)商協(xié)議(agreement protocol)的處理. 這個(gè)邏輯很自然, 因?yàn)閷懖僮魇且S持多個(gè)Server之間數(shù)據(jù)的一致性的.
那么核心問(wèn)題就是, 這個(gè)所謂的agreement protocol是如何保證寫操作全局一致的呢? 它有兩個(gè)策略:
ZooKeeper在消息通信上使用了一個(gè)自定義的原子消息協(xié)議, 消息協(xié)議的原子性保證了ZooKeeper中每個(gè)Server, 無(wú)論是爹還是兒子, 數(shù)據(jù)庫(kù)中存儲(chǔ)的數(shù)據(jù)都是實(shí)時(shí)的. 當(dāng)?shù)盏接蓛鹤舆f交的一個(gè)寫請(qǐng)求后, 會(huì)做如下事情:
1.1.6 ZooKeeper的用法
ZooKeeper的編程接口十分簡(jiǎn)單, 這部分內(nèi)容會(huì)在后續(xù)章節(jié)涉及到. 簡(jiǎn)單意味著API數(shù)量少, 容易被理解, 但也有一點(diǎn)壞處, 就是接口能實(shí)現(xiàn)的功能十分基本基礎(chǔ), 要實(shí)現(xiàn)高級(jí)一點(diǎn)的功能, 你需要自己寫邏輯, 當(dāng)然這也不會(huì)十分困難.
1.1.7 ZooKeeper的性能
前面吹了那么多ZooKeeper的好處, 是時(shí)候用一些證據(jù)來(lái)支撐前面吹的逼了. ZooKeeper的實(shí)際性能到底有多少? 我相信很多傳統(tǒng)的C/C++后臺(tái)開(kāi)發(fā)人員在看到ZooKeeper是跑在JVM上的Java程序的時(shí)候都在內(nèi)心默默的鄙視了一下ZooKeeper.
但反直覺(jué)的是, 根據(jù)雅虎的ZooKeeper研發(fā)團(tuán)隊(duì)報(bào)告, ZooKeeper的性能確實(shí)十分強(qiáng)勁, 但性能強(qiáng)勁是有前提條件的: 那就是對(duì)數(shù)據(jù)的讀取操作量遠(yuǎn)大于數(shù)據(jù)寫入操作量. 當(dāng)然作為一個(gè)供其它應(yīng)用程序使用的協(xié)調(diào)服務(wù), 讀量大于寫量應(yīng)該是一個(gè)典型現(xiàn)象.
下圖是ZooKeeper的性能圖表, 其中橫軸是指請(qǐng)求量中讀操作的占比, 縱軸是每秒能處理的請(qǐng)求量. 顏色不同的線代表ZooKeeper的實(shí)例個(gè)數(shù)(顯然每個(gè)實(shí)例獨(dú)占一臺(tái)機(jī)器). 可以直觀的看到, 在讀操作占比超過(guò)80%后, ZooKeeper的吞吐量就起飛了. 并且5個(gè)實(shí)例構(gòu)成的ensemble在性能的提升上對(duì)比3個(gè)實(shí)例構(gòu)成的ensemble有顯著提升, 但超過(guò)5之后, 增加實(shí)例個(gè)數(shù)對(duì)ZooKeeper的整體性能提升就不是很明顯了. 當(dāng)然這只是一個(gè)參考圖表, 應(yīng)用實(shí)施的時(shí)候, 各家都有各家的特殊國(guó)情, 還需要自行探究.
上面這邊圖的測(cè)試現(xiàn)場(chǎng)大致是這樣的:
這大致能解釋為什么ensemble中實(shí)例的數(shù)量從3到5的時(shí)候, 性能有一個(gè)飛躍: 因?yàn)?個(gè)實(shí)例組成的ensemble中, 干活的只有兩個(gè), 另外一個(gè)是爹. 變成5個(gè)實(shí)例組成的ensemble后, 干活的就有四個(gè)了, 提升了一倍.
1.1.8 ZooKeeper的可靠性
除開(kāi)吞吐量外, 使用者另外關(guān)心的一個(gè)指標(biāo)就是可靠性, 下面這張圖依然出自雅虎團(tuán)隊(duì), 搭建了一個(gè)7個(gè)實(shí)例組成的ensemble, 然后用910個(gè)client去瘋狂懟這個(gè)ZooKeeper集群, 懟ZooKeeper時(shí)其它參數(shù)和上面的性能測(cè)試一致, 不同的是寫操作的比例為30%, 讀操作為70%. 在懟的過(guò)程中, 依次手動(dòng)模塊了如下情況:
圖中橫軸是時(shí)間, 單位是秒, 縱軸是吞吐量. 圖中帶圓圈的數(shù)字分別代表了有上面對(duì)應(yīng)的錯(cuò)誤發(fā)生.
client共有910個(gè), 所以比起上一張圖來(lái)說(shuō), 吞吐圖下降了不少. 從圖中可以看出:
1.1.9 ZooKeeper的應(yīng)用前景
ZooKeeper的版本號(hào), 直至我翻譯這個(gè)文檔的時(shí)候, 穩(wěn)定版已經(jīng)到3.4.12了, beta版已經(jīng)到3.5.4了. 能迭代到這個(gè)程序說(shuō)明Apache對(duì)這個(gè)項(xiàng)目的態(tài)度已經(jīng)顯然不止于玩票了. 實(shí)際上ZooKeeper已經(jīng)成功的應(yīng)用在許多業(yè)界的產(chǎn)品上了. 雅虎用ZooKeeper做Message Broker的協(xié)調(diào)和錯(cuò)誤恢復(fù)服務(wù), 這應(yīng)當(dāng)是ZooKeeper能傍上的最粗的大腿了.雅虎的這個(gè)消息隊(duì)列是一個(gè)很基礎(chǔ)的發(fā)布-訂閱消息系統(tǒng), 規(guī)模很大. 受這個(gè)成功案例的影響, 雅虎里的一標(biāo)廣告系統(tǒng)在在用ZooKeeper做可靠性服務(wù).
1.2 快速上手指南
這一小節(jié)主要是面向開(kāi)發(fā)者的, 大致閱讀本小節(jié)的內(nèi)容, 能讓你快速上手, 在你的代碼中使用上ZooKeeper的功能, 并且為了配合自測(cè), 這一小節(jié)大致會(huì)簡(jiǎn)單的講一下如何搭建一個(gè)由單個(gè)實(shí)例組成的ZooKeeper ensemble(僅供開(kāi)發(fā)調(diào)試使用的部署模式), 并且講幾個(gè)小命令用以檢測(cè)你的ZooKeeper是否搭建成功, 再附上一些代碼片斷幫助你直觀的理解ZooKeeper接口的用法.
這一節(jié)不會(huì)涉及生產(chǎn)環(huán)境中多Server模式的部署方法, 也不會(huì)涉及部署中的詳細(xì)配置參數(shù)等. 這一節(jié)只是一個(gè)快速上手指南: 并且是面向開(kāi)發(fā)人員的快速上手指南.
1.2.1 系統(tǒng)要求
請(qǐng)參考第四章
1.2.2 下載ZooKeeper
從這里下載ZooKeeper的最新的穩(wěn)定版.
1.2.3 單實(shí)例模式
搭一個(gè)單實(shí)例模式的ZooKeeper很簡(jiǎn)單,
另外需要注意的還有以下幾點(diǎn):
1.2.4 管理ZooKeeper的存儲(chǔ)
ZooKeeper有兩個(gè)地方使用到了本地磁盤存儲(chǔ), 如果你仔細(xì)閱讀了之前的章節(jié)的話, 會(huì)知道這兩個(gè)點(diǎn)分別是:
生產(chǎn)環(huán)境建議這兩個(gè)東西分開(kāi)存儲(chǔ), 并且最好是物理上分開(kāi)存儲(chǔ): 分別使用一塊獨(dú)立的硬盤. 但具體細(xì)節(jié)這一章節(jié)就不說(shuō)了, 后續(xù)在運(yùn)維相關(guān)的章節(jié)會(huì)講到.
1.2.5 連接至ZooKeeper服務(wù)
執(zhí)行安裝包里的這個(gè)腳本可以連接至上面搭建好的ZooKeeper本地單實(shí)例服務(wù)上去:
$ zookeeper/bin/zkCli.sh -server 127.0.0.1:2181這打開(kāi)了一個(gè)交互式命令行程序, 連接成功后, 大致會(huì)有類似于以下的輸出:
Connecting to localhost:2181 log4j:WARN No appenders could be found for logger (org.apache.zookeeper.ZooKeeper). log4j:WARN Please initialize the log4j system properly. Welcome to ZooKeeper! JLine support is enabled [zkshell: 0]在這個(gè)命令行程序內(nèi)部, 鍵入help獲取一些基本命令的用法, 比如這樣:
[zkshell: 0] help ZooKeeper host:port cmd argsget path [watch]ls path [watch]set path data [version]delquota [-n|-b] pathquitprintwatches on|offcreatepath data aclstat path [watch]listquota pathhistorysetAcl path aclgetAcl pathsync pathredo cmdnoaddauth scheme authdelete path [version]setquota -n|-b val path類似于mysql的命令行工具, 你可以在這個(gè)命令行工具里向ZooKeeper服務(wù)發(fā)出一些指令, 比如你想查看目前ZooKeeper的層級(jí)名稱空間里都存儲(chǔ)了些啥玩意, 你可以使用ls命令
[zkshell: 8] ls / [zookeeper]初始狀態(tài)下, 層級(jí)名稱空間里啥都沒(méi)有, 根/下只有一個(gè)名為zookeeper的節(jié)點(diǎn), 也就是znode. 你可以使用命令create來(lái)創(chuàng)建一個(gè)節(jié)點(diǎn), 也就是znode, 像下面這樣
[zkshell: 9] create /zk_test my_data Created /zk_test上面這個(gè)命令創(chuàng)建了一個(gè)znode, 這個(gè)znode的路徑是/zk_text, 并且把一個(gè)字符串"my_data"存在了這個(gè)znode中. 現(xiàn)在再用ls查看節(jié)點(diǎn), 輸出會(huì)類似如下:
[zkshell: 11] ls / [zookeeper, zk_test]現(xiàn)在根名稱空間/下有兩個(gè)節(jié)點(diǎn), 一個(gè)是默認(rèn)存在的zookeeper, 一個(gè)是我們剛才創(chuàng)建好的zk_text節(jié)點(diǎn).
如果你想取出znode中的數(shù)據(jù), 可以使用get命令來(lái)獲取, 如下:
[zkshell: 12] get /zk_test my_data cZxid = 5 ctime = Fri Jun 05 13:57:06 PDT 2009 mZxid = 5 mtime = Fri Jun 05 13:57:06 PDT 2009 pZxid = 5 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0 dataLength = 7 numChildren = 0輸出了不少東西, 正如我們?cè)诤?jiǎn)介里提到的那樣, 當(dāng)你向znode寫入數(shù)據(jù)時(shí), znode不光會(huì)保存數(shù)據(jù)本身, 還會(huì)保存一些其它玩意, 比如數(shù)據(jù)的創(chuàng)建時(shí)間, 數(shù)據(jù)的版本號(hào)之類的, 上面的輸出很豐富, 第一行當(dāng)然是我們?cè)趧?chuàng)建/zk_text節(jié)點(diǎn)時(shí)寫入的字符串"my_data", 余下的行則是與這個(gè)數(shù)據(jù)有關(guān)的其它信息. 詳細(xì)細(xì)節(jié)這里先略過(guò).
當(dāng)你想修改一個(gè)znode下的數(shù)據(jù)的時(shí)候, 你可以使用set命令, 就像下面這樣, 我們把上面/zk_text節(jié)點(diǎn)里的數(shù)據(jù), 從原先的"my_data"字符串, 修改為新字符串"junk":
[zkshell: 14] set /zk_test junk cZxid = 5 ctime = Fri Jun 05 13:57:06 PDT 2009 mZxid = 6 mtime = Fri Jun 05 14:01:52 PDT 2009 pZxid = 5 cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0 dataLength = 4 numChildren = 0修改數(shù)據(jù)也會(huì)輸出節(jié)點(diǎn)的額外信息, 接下來(lái)再用get命令查看一下剛才的修改效果:
[zkshell: 15] get /zk_test junk cZxid = 5 ctime = Fri Jun 05 13:57:06 PDT 2009 mZxid = 6 mtime = Fri Jun 05 14:01:52 PDT 2009 pZxid = 5 cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0 dataLength = 4 numChildren = 0可以看到, 數(shù)據(jù)已經(jīng)被修改了.
當(dāng)你想刪除一個(gè)znode的時(shí)候, 你可以使用delete命令, 就像下面這樣, 我們刪除上面我們創(chuàng)建的/zk_text節(jié)點(diǎn), 在刪除后再用ls命令查看一下刪除效果
[zkshell: 16] delete /zk_test [zkshell: 17] ls / [zookeeper] [zkshell: 18]你看, 這樣是不是就很直觀, 簡(jiǎn)介里說(shuō)了一大堆, 比不上你自己動(dòng)手感受一下. 在這個(gè)交互式命令行程序里的各個(gè)命令, 其它都對(duì)應(yīng)著ZooKeeper API里的各個(gè)接口, 有了上面的直觀認(rèn)識(shí), 再參照著編程手冊(cè)使用ZooKeeper就很容易理解了.
1.2.6 在代碼中使用ZooKeeper
ZooKeeper對(duì)外提供的API有Java版和C語(yǔ)言兩個(gè)版本, 功能上是完全一樣的. C語(yǔ)言的API還有兩個(gè)子版本: 一個(gè)用于在單線程環(huán)境中使用, 一個(gè)用于在多線程環(huán)境中使用. 更多的具體信息請(qǐng)參考后續(xù)章節(jié).
1.2.7 搭建多實(shí)例的ZooKeeper ensemble
生產(chǎn)環(huán)境中顯然不能使用單實(shí)例的ZooKeeper, 這里簡(jiǎn)單提一下如何搭建多實(shí)例構(gòu)成的ZooKeeper ensemble. 多個(gè)實(shí)例構(gòu)成的ensemble中, 每個(gè)ZooKeeper server進(jìn)程都有自己的內(nèi)存數(shù)據(jù)庫(kù), 但所有實(shí)例的內(nèi)存數(shù)據(jù)庫(kù)里的內(nèi)容是一毛一樣的, 這也是為什么官方文檔里把多實(shí)例模式的ZooKeeper ensemble稱為Replicated ZooKeeper的原因. 有一點(diǎn)小蛋疼的地方是, 每個(gè)ZooKeeper server進(jìn)程在啟動(dòng)時(shí)都需要一個(gè)配置文件, 在多實(shí)例ZooKeeper ensemble中, 這多個(gè)實(shí)例使用的配置文件必須一毛一樣, 多個(gè)實(shí)例正常情況下應(yīng)當(dāng)分布在多個(gè)不同的機(jī)器上, 所以這個(gè)配置文件的一致性嘛, 就需要運(yùn)維部署人員手工維護(hù)了.
在這篇翻譯的文檔里, 我始終堅(jiān)持把這種多個(gè)ZooKeeper server進(jìn)程構(gòu)成的ZooKeeper服務(wù)稱為多實(shí)例模式ZooKeeper, 而不叫集群, 原因是: ZooKeeper并沒(méi)有嚴(yán)格限制你, 必須每個(gè)ZooKeeper server獨(dú)占一臺(tái)機(jī)器. 雖然從邏輯上講, 多實(shí)例模式下應(yīng)當(dāng)為每個(gè)ZooKeeper server分配一臺(tái)獨(dú)立的機(jī)器, 但你非要在一臺(tái)機(jī)器上啟動(dòng)100個(gè)ZooKeeper server, 這是完全行得通的.
在多實(shí)例模式下, 實(shí)例的數(shù)量是有最低要求的: 最少要有3個(gè)實(shí)例. 并且強(qiáng)烈建議你, 實(shí)例的數(shù)目設(shè)定為奇數(shù): 我們?cè)诤?jiǎn)介中提到過(guò), ZooKeeper服務(wù)運(yùn)行時(shí), 如果有少量實(shí)例掛掉了, 整個(gè)ZooKeeper服務(wù)還會(huì)正常運(yùn)轉(zhuǎn). 這里對(duì)于少量的定義, 其實(shí)就是看掛掉的實(shí)例數(shù)量有沒(méi)有達(dá)到二分之一. 奇數(shù)數(shù)目的實(shí)例, 比較容易判定二分之一.
多實(shí)例模式下的配置文件, 依然建議放在zookeeper/conf/zoo.cfg中, 內(nèi)容與單實(shí)例模式稍有不同
tickTime=2000 dataDir=/var/lib/zookeeper clientPort=2181 initLimit=5 syncLimit=2 server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888其中tickTime, dataDir, clientPort三個(gè)配置項(xiàng)的含義這里不再重復(fù), 新增的字段的含義如下:
1.2.8 簡(jiǎn)單的優(yōu)化建議
為了獲取更好的性能, 建議在配置文件中額外配置一個(gè)配置項(xiàng), 名為dataLogDir, 值為一個(gè)目錄地址. 配置該配置項(xiàng)之后, 會(huì)導(dǎo)致當(dāng)前實(shí)例會(huì)將事務(wù)日志寫向?qū)?yīng)地址. 否則會(huì)直接寫向dataDir中去.
更多的優(yōu)化建議, 與更細(xì)節(jié)的多實(shí)例ZooKeeper部署指導(dǎo)會(huì)在后續(xù)運(yùn)維章節(jié)仔細(xì)說(shuō)明.
2. 面向開(kāi)發(fā)者的文檔
// TODO
3. BookKeeper
// TODO
4. 面向系統(tǒng)管理員與運(yùn)維人員的文檔
4.1 系統(tǒng)管理員指南
4.1.1 部署
4.1.1.1 系統(tǒng)要求
支持的操作系統(tǒng)平臺(tái)
ZooKeeper下有多個(gè)組件. 有些組件在各大平臺(tái)上都能跑, 有些則只能跑在指定的一部分平臺(tái)上. 這些組件包括:
下表展示了各個(gè)組件在各個(gè)操作系統(tǒng)平臺(tái)上的受支持程度:
| GNU/Linux | 可用于開(kāi)發(fā)及生產(chǎn) | 可用于開(kāi)發(fā)及生產(chǎn) | 可用于開(kāi)發(fā)及生產(chǎn) | 可用于開(kāi)發(fā)及生產(chǎn) |
| Solaris | 可用于開(kāi)發(fā)及生產(chǎn) | 可用于開(kāi)發(fā)及生產(chǎn) | 不支持 | 不支持 |
| FreeBSD | 可用于開(kāi)發(fā)及生產(chǎn) | 可用于開(kāi)發(fā)及生產(chǎn) | 不支持 | 不支持 |
| Windows | 可用于開(kāi)發(fā)及生產(chǎn) | 可用于開(kāi)發(fā)及生產(chǎn) | 不支持 | 不支持 |
| Max OS X | 僅可用于開(kāi)發(fā) | 僅可用于開(kāi)發(fā) | 不支持 | 不支持 |
其它沒(méi)提到的操作系統(tǒng)平臺(tái)就自求多福吧.
需要的軟件
ZooKeeper是用Java寫的, 所以需要一個(gè)版本在1.6或更高的JDK. 對(duì)于多實(shí)例模式的部署建議使用多臺(tái)機(jī)器.
4.1.1.2 多實(shí)例模式
我們?cè)诘谝徽碌目焖偕鲜种改现刑岬竭^(guò), 生產(chǎn)環(huán)境建議使用多實(shí)例模式部署ZooKeeper, 并且建議單個(gè)實(shí)例獨(dú)占一臺(tái)機(jī)器, 且建議實(shí)例的個(gè)數(shù)為奇數(shù). 個(gè)中緣由這里不再重復(fù).
下面是如何部署多實(shí)例模式中的一個(gè)實(shí)例的步驟. 在多臺(tái)機(jī)器上重復(fù)以上步驟, 就能搭起一個(gè)多實(shí)例模式的ZooKeeper服務(wù).
4.1.1.3 單實(shí)例模式
單實(shí)例模式一般僅用于開(kāi)發(fā)自測(cè)使用, 具體的部署步驟在1.2.3 單實(shí)例模式章節(jié)有描述, 請(qǐng)翻回去看.
4.1.2 系統(tǒng)管理
4.1.2.1 部署設(shè)計(jì)
ZooKeeper的可靠性依賴于兩個(gè)假設(shè)
要使第一個(gè)假設(shè)盡可能的成立, 你可以做如下的事情:
要使第二個(gè)假設(shè)盡可能的成立, 你可以做如下的事情:
4.1.2.2 Provisioning
// TODO, 官方文檔這里留空了
4.1.2.3 Things to Consider: ZooKeeper Strengths and Limitations
// TODO, 官方文檔這里留空了
4.1.2.4 Administering
// TODO, 官方文檔這里留空了
4.1.2.5 運(yùn)維
對(duì)于長(zhǎng)期運(yùn)行的ZooKeeper ensemble來(lái)說(shuō), 運(yùn)維工作是必須做的, 運(yùn)維人員需要注意以下幾點(diǎn):
清理磁盤文件
ZooKeeper中有兩處使用到了磁盤: 事務(wù)日志與內(nèi)存數(shù)據(jù)庫(kù)快照. ZooKeeper名稱空間里的節(jié)點(diǎn)發(fā)生變更的時(shí)候, 就會(huì)有內(nèi)容寫入事務(wù)日志. 通常情況下, 當(dāng)單個(gè)事務(wù)日志文件變的越來(lái)越大的時(shí)候, 事務(wù)日志就需要?jiǎng)?chuàng)建一個(gè)新的文件. 但在創(chuàng)建新的事務(wù)日志文件之前, ZooKeeper會(huì)先把當(dāng)前的內(nèi)存數(shù)據(jù)庫(kù)的狀態(tài)寫入磁盤先做快照, 然后再生成一個(gè)新的事務(wù)日志文件. 這樣就保證了快照文件和事務(wù)日志文件是一一對(duì)應(yīng)的. 但快照落地需要時(shí)間, 在快照落地期間如果還有事務(wù)來(lái)臨, 那么這部分事務(wù)的日志依然會(huì)寫向舊的事務(wù)日志文件里. 這就導(dǎo)致, 快照文件對(duì)應(yīng)的那個(gè)事務(wù)日志文件里, 存儲(chǔ)的事務(wù)日志可能要比當(dāng)前快照文件要新.
ZooKeeper server進(jìn)程在默認(rèn)啟動(dòng)的情況下, 是不會(huì)自動(dòng)刪除事務(wù)日志文件和快照文件的. 當(dāng)然這是可配置的, 配置項(xiàng)分別是autopurge.snapRetainCount和autopurge.purgeInterval. 這兩個(gè)配置項(xiàng)的具體含義在4.1.2.10章節(jié)有詳細(xì)描述. 但需要注意: 如果你要這樣做, 那么最好為每臺(tái)部署的機(jī)器提供不同的配置值, 除非這些機(jī)器的規(guī)格是完全一毛一樣的!
除過(guò)在配置文件中設(shè)定, 還有一種方法就是調(diào)用一個(gè)ZooKeeper提供的小工具, 大致如下:
java -cp zookeeper.jar:lib/log4j-1.2.15.jar:conf org.apache.zookeeper.server.PurgeTxnLog <dataDir> <snapDir> -n <count>其中<dataDir>是事務(wù)日志的保存目錄, <snapDir>是快照文件的保存目錄, <count>是要保留的個(gè)數(shù). 建議大于3. 運(yùn)行該命令后, 除過(guò)最近的<count>對(duì)事務(wù)日志文件與快照文件, 其它文件都將被刪除. 這是一個(gè)一次性命令. 如果你想定期清理, 那么只能自己寫個(gè)腳本咯.
注意兩點(diǎn):
清理運(yùn)行日志
ZooKeeper用log4j來(lái)輸出運(yùn)行日志. 如果要更改運(yùn)行日志的相關(guān)配置, 你需要獨(dú)立為log4j提供配置文件. 建議使用log4j提供的滾動(dòng)日志特性, 這樣就免去了清理運(yùn)行日志的問(wèn)題. 更多詳細(xì)信息請(qǐng)參閱4.1.2.8 運(yùn)行日志
4.1.2.6 監(jiān)控ZooKeeper server進(jìn)程的死活
ZooKeeper的server進(jìn)程在錯(cuò)誤發(fā)生的時(shí)候會(huì)立即自殺, ZooKeeper的設(shè)計(jì)哲學(xué)是這樣的:
所以搞一個(gè)監(jiān)控進(jìn)程, 在實(shí)例進(jìn)程掛掉之后將其立即拉起是一個(gè)很好的做法. 比如daemontools或SMF. 還比如我們親愛(ài)的織云.
4.1.2.7 監(jiān)控ZooKeeper server服務(wù)的狀態(tài)
要監(jiān)控ZooKeeper服務(wù)的狀態(tài), 有兩個(gè)選擇
4.1.2.8 運(yùn)行日志
ZooKeeper使用log4j 1.2來(lái)輸出運(yùn)行日志. 默認(rèn)的配置文件在zookeeper/conf/log4j.properties中. log4j的配置文件要求要么放在工作目錄里, 要么放在類路徑里.
詳情請(qǐng)查看log4j的官方手冊(cè)
4.1.2.9 問(wèn)題定位
由于文件損壞導(dǎo)致實(shí)例不能啟動(dòng)
ZooKeeper的server進(jìn)程在事務(wù)日志文件被損壞的情況下是起不來(lái)的. 這時(shí)運(yùn)行日志會(huì)說(shuō)在載入ZooKeeper database時(shí)出現(xiàn)IOException. 這種情況下, 你需要做的是:
這種情況是當(dāng)前實(shí)例的事務(wù)文件損壞, 不能重建內(nèi)存數(shù)據(jù)庫(kù), 刪除掉事務(wù)日志和數(shù)據(jù)庫(kù)快照后, 當(dāng)前的實(shí)例在重啟后會(huì)通過(guò)其它實(shí)例拉取內(nèi)在數(shù)據(jù)庫(kù), 重建事務(wù)日志和快照文件.
4.1.2.10 配置參數(shù)
ZooKeeper的行為受配置文件影響. 所有同一個(gè)ensemble中的實(shí)例建議使用完全相同的配置文件. 但使用完全相同的配置文件有一個(gè)前提條件: 就是所有實(shí)例所屬的機(jī)器上的磁盤布局是相同的. 磁盤布局不同意味著不同的機(jī)器下的實(shí)例在配置dataDir和dataLogDir的時(shí)候配置值可能有差異, 但除此之外, 一個(gè)ensemble中的所有實(shí)例的配置文件必須保證server.x=xxxx這些配置值是完全一致的.
最小配置
下面列出來(lái)的是要讓ensemble正常工作, 每個(gè)實(shí)例都需要配置的配置項(xiàng)
| clientPort | 當(dāng)前實(shí)例對(duì)外提供服務(wù)的端口號(hào). 即是client通過(guò)該端口號(hào)連接到該實(shí)例上. 建議所有實(shí)例都配置為相同的值. |
| dataDir | 內(nèi)在數(shù)據(jù)庫(kù)快照的存儲(chǔ)地址. 如果沒(méi)有配置dataLogDir的話, 該目錄還會(huì)存儲(chǔ)事務(wù)日志 |
| tickTime | ZooKeeper中計(jì)量時(shí)間的最基本單位. 配置值的單位是毫秒 |
高級(jí)可選配置
下面列出來(lái)的是一此可選配置, 屬于高級(jí)選項(xiàng). 你可以用這些配置項(xiàng)進(jìn)一步個(gè)性化ZooKeeper server的行為. 其中一些配置項(xiàng)的值可以通過(guò)在啟動(dòng)server進(jìn)程的時(shí)候?qū)懭隞ava 系統(tǒng)屬性來(lái)設(shè)置.
| dataLogDir | 無(wú) | 事務(wù)日志的寫入地址 |
| globalOutstandingLimit | zookeeper.globalOutstandingLimit | client向ZooKeeper server遞交請(qǐng)求的速度可以比ZooKeeper server處理請(qǐng)求的速度快, 特別是有多個(gè)client訪問(wèn)同一個(gè)ZooKeeper server的時(shí)候. 通常情況下對(duì)于不能及時(shí)處理的請(qǐng)求, server都會(huì)將其緩存到隊(duì)列中. 但這個(gè)隊(duì)列也不是無(wú)限長(zhǎng)的, 這個(gè)配置項(xiàng)就是在設(shè)置這個(gè)隊(duì)列的長(zhǎng)度, 默認(rèn)值是1000, 超過(guò)隊(duì)列長(zhǎng)度后, 再發(fā)請(qǐng)求請(qǐng)求就會(huì)被丟棄 |
| preAllocSize | zookeeper.preAllocSize | 為了避免頻繁的seek操作, ZooKeeper的事務(wù)日志文件默認(rèn)是以64M為基本單位的. 設(shè)置這個(gè)值就可以改寫這個(gè)默認(rèn)的塊大小, 這個(gè)配置項(xiàng)的單位是kb |
| snapCount | zookeeper.snapCount | 默認(rèn)值是100000, 這是指每向事務(wù)日志里記錄十萬(wàn)個(gè)事務(wù), 內(nèi)存數(shù)據(jù)庫(kù)就會(huì)被快照一次, 同時(shí)事務(wù)日志會(huì)開(kāi)新文件. 但為了避免所有的ZooKeeper server在同一時(shí)刻落地快照更換事務(wù)日志文件, 為了把這個(gè)操作錯(cuò)開(kāi), 所以真實(shí)的值是 位于區(qū)間 [50001, 100000] 區(qū)間的一個(gè)隨機(jī)數(shù). 即是 [snapCount/2 + 1, snapCount]區(qū)間 |
| maxClientCnxns | 無(wú) | 同一個(gè)個(gè)client是可以和同一個(gè)ZooKeeper ensemble之間建立多個(gè)連接的, 這個(gè)配置項(xiàng)就是在限制同一個(gè)client與ZooKeeper ensemble之間建立的連接數(shù). 這可以有效預(yù)防DoS攻擊, 還可以預(yù)防ZooKeeper server所在的機(jī)器文件描述符耗盡. 這個(gè)值默認(rèn)是60, 當(dāng)這個(gè)值被設(shè)置為0時(shí), 是取消掉這個(gè)限制的意思 |
| clientPortAddress | 無(wú) | 這是一個(gè)3.3版本后的新配置項(xiàng), 這是一個(gè)IP地址, 當(dāng)配置了之后, ZooKeeper server在監(jiān)聽(tīng)端口的時(shí)候, 就會(huì)綁定到這個(gè)地址上. 而默認(rèn)情況下, 在監(jiān)聽(tīng)clientPort端口的時(shí)候, 綁定的是ANYADDR |
| minSessionTimeout | 無(wú) | 這也是一個(gè)3.3版本后的新配置項(xiàng), 這個(gè)配置項(xiàng)的單位是毫秒, 而不是tickTime. 指的是會(huì)話超時(shí)r的最小時(shí)間, 默認(rèn)的會(huì)話超時(shí)時(shí)間是2個(gè)tickTime. 這個(gè)值可以和client協(xié)商. |
| maxSessionTimeout | 無(wú) | 這依然是一個(gè)3.3版本后的新配置項(xiàng). 和minSessionTimeout類似, 但這是會(huì)話超時(shí)的最大時(shí)間. 默認(rèn)是20個(gè)tickTime |
| fsync.warningthresholdms | zookeeper.fsync.warningthresholdms | 3.3.4版本后的一個(gè)新配置項(xiàng). 這是一個(gè)時(shí)間量, 單位是毫秒, 默認(rèn)是1000, 也就是一秒. 這指的是當(dāng)fsync 事務(wù)日志的耗時(shí)大于1秒時(shí), 就會(huì)向運(yùn)行日志里輸出一條warning日志. 注意這個(gè)配置項(xiàng)只能通過(guò)Java系統(tǒng)屬性設(shè)置 |
| autopurge.snapRetainCount | 無(wú) | 3.4版本后的一個(gè)新配置項(xiàng), 當(dāng)設(shè)置了該配置項(xiàng)后, ZooKeeper將會(huì)自動(dòng)清理快照文件和對(duì)應(yīng)的事務(wù)日志文件. 僅保留最近的autopurge.snapRetainCount個(gè). 默認(rèn)值是3, 最小值是3, 不能設(shè)置比3更小的值 |
| autopurge.purgeInterval | 無(wú) | 這是一個(gè)開(kāi)關(guān)配置項(xiàng), 也是一個(gè)時(shí)間配置項(xiàng). 當(dāng)設(shè)置為0的時(shí)候, 是關(guān)閉自動(dòng)清理快照和事務(wù)日志文件的功能的意思. 當(dāng)設(shè)置為1或更高的值時(shí), 是指每隔autopurge.purgeInterval個(gè)小時(shí), 就執(zhí)行一次自動(dòng)清理任務(wù) |
| syncEnabled | zookeeper.observer.syncEnabled | 3.4.6有這個(gè)配置項(xiàng), 3.5.0和更高的版本有. observer在默認(rèn)情況下會(huì)和participants一樣記錄事務(wù)日志, 落地快照. 將這個(gè)值設(shè)置為false就是關(guān)掉了observer記錄事務(wù)日志落地快照的行為, 默認(rèn)是true. observer和participants的概念會(huì)在4.4章節(jié)介紹 |
多實(shí)例模式下的配置項(xiàng)
下面列出來(lái)的配置項(xiàng)是多實(shí)例模式下的一些配置項(xiàng). 有一些配置項(xiàng)可以通過(guò)在啟動(dòng)server進(jìn)程的時(shí)候?qū)懭隞ava系統(tǒng)屬性來(lái)設(shè)置.
| electionAlg | 無(wú) | 選爹算法. 0 - 原始的基于UDP的選爹算法. 1 - 不帶身份認(rèn)證的, 基于UDP的快速選爹算法. 2 - 帶身份認(rèn)證的, 基于UDP的快速選爹算法. 3 - 基于TCP的快速選爹算法. 默認(rèn)值是3. 其中, 0, 1, 2被官方文檔標(biāo)記為了deprecated, 也就是說(shuō), 除非有特殊需求, 不要?jiǎng)邮峙渲眠@個(gè)配置項(xiàng) |
| initLimit | 無(wú) | 時(shí)間值, 單位是tickTime, 指的是兒子與爹建立連接并同步數(shù)據(jù)的超時(shí)時(shí)限. 當(dāng)ZooKeeper管理的數(shù)據(jù)特別多特別大的話, 建議適當(dāng)增加這個(gè)配置值. 這個(gè)配置項(xiàng)沒(méi)有默認(rèn)值, 所以配置文件里必寫 |
| leaderServes | zookeeper.leaderServes | 控制爹是否也向client提供服務(wù), 默認(rèn)是允許的. 如果client的業(yè)務(wù)比較復(fù)雜的話, 建議關(guān)閉這個(gè)功能, 讓爹專心管兒子 |
| server.x=[hostname]:nnnn[:nnnn] | 無(wú) | ensemble中的每一個(gè)實(shí)例都要這樣登記在配置文件里. x是dataLog目錄下的myid文件里的數(shù)字, 即是實(shí)例的編號(hào). hostname是對(duì)應(yīng)的實(shí)例所在的機(jī)器名, 或者直接寫成IP地址, 前一個(gè)nnnn是用于爹和兒子通信用的. 第二個(gè)nnnn 是用于選舉爹用的. |
| syncLimit | 無(wú) | 時(shí)間值, 單位是tickTime, 兒子向爹報(bào)平安的時(shí)限, 如果兒子在超過(guò)這個(gè)時(shí)間值后沒(méi)有給爹報(bào)平安, 爹就認(rèn)為兒子死了. 關(guān)于initLimit和syncLimit這兩個(gè)配置項(xiàng)之間的不同, 請(qǐng)參考這個(gè)郵件列表里的解釋. 官方文檔里寫的簡(jiǎn)直讓人懷疑是不是復(fù)制粘貼過(guò)來(lái)的. |
| group.x=nnnn[:nnnn] | 無(wú) | 給server實(shí)例分組. x是組編號(hào). nnnn則是實(shí)例編號(hào). 注意如果你給ensemble中的實(shí)例分組的話, 各個(gè)組之間不能有交集, 并且要保證所有組的并集就是整個(gè)ZooKeeper ensemble. (換句話說(shuō)你不能單獨(dú)為其中某一些實(shí)例分組, 所有的實(shí)例都必須從屬于一個(gè)組), 這里有一個(gè)例子 |
| weight.x=nnnn | 無(wú) | 為一個(gè)組設(shè)置權(quán)重. ZooKeeper只有在少量的一些場(chǎng)合才需要考慮權(quán)重, 已知的兩個(gè)場(chǎng)合, 一個(gè)是選爹的時(shí)候, 權(quán)重影響實(shí)例投票的影響力, 另外一個(gè)是atomic broadcast protocol. 默認(rèn)情況下權(quán)重值是1 |
| cnxTimeout | zookeeper.cnxTimeout | 時(shí)間值, 單位是秒. 爹選出來(lái)后, 所有人都需要知道爹是誰(shuí), 這個(gè)時(shí)間就是選爹時(shí)每個(gè)機(jī)器都會(huì)把選爹端口打開(kāi)的時(shí)間, 在這個(gè)時(shí)間內(nèi)應(yīng)當(dāng)有一個(gè)結(jié)果, 并且結(jié)果將通知到每個(gè)實(shí)例的選爹端口上. 這個(gè)配置項(xiàng)僅在electionAlg的值為3時(shí)才有用. 默認(rèn)值是5秒 |
| 4lw.commands.whitelist | zookeeper.4lw.commands.whitelist | 四字命令白名單, 未出現(xiàn)在名單上的四字命令, ZooKeeper將不處理. 默認(rèn)值包括除過(guò)wchp和wchc兩個(gè)之外的所有四字命令. 如果要設(shè)置這個(gè)配置項(xiàng)的值, 注意各個(gè)四字命令之間要以逗號(hào)區(qū)分, 比如: r4lw.commands.whitelist=stat, ruok, conf, isro, 如果你需要開(kāi)啟所有四字命令, 可以簡(jiǎn)單的使用通配符4lw.commands.whitelist=* |
| ipReachableTimeout | zookeeper.ipReachableTimeout | 3.4.11版本后的新配置項(xiàng), 時(shí)間值, 單位是毫秒. 當(dāng)一個(gè)server的hostname不是IP地址時(shí), 并且恰巧DNS服務(wù)或者h(yuǎn)osts表里這個(gè)名字后面掛著多個(gè)ip地址時(shí), 這個(gè)值就有用了. 默認(rèn)情況下, ZooKeeper會(huì)默認(rèn)使用名字解析出來(lái)的第一個(gè)IP地址, 而不檢查這個(gè)IP是否可達(dá), 而如果這個(gè)值設(shè)置成了一個(gè)大于0的值, 那么ZooKeeper就會(huì)使用InetAddress.isReachable(ipReachableTimeout)來(lái)判斷這個(gè)IP是否可達(dá), 如果不可達(dá), 就換下一個(gè), 如果全都不可達(dá), 那么就會(huì)絕望的使用第一個(gè)IP地址 |
| tcpKeepAlive | zookeeper.tcpKeepAlive | 3.4.11版本后的新配置項(xiàng). 如果這個(gè)配置項(xiàng)被設(shè)置為true, 那么server之間用來(lái)選舉的TCP連接就會(huì)被置為長(zhǎng)連接, 默認(rèn)情況下這個(gè)值是false |
身份認(rèn)證與授權(quán)相關(guān)的配置項(xiàng)
下面這些配置項(xiàng)與身份認(rèn)證授權(quán)相關(guān).
為了避免看不懂下面的配置項(xiàng)都在干嘛, 先大致說(shuō)一下Zookeeper里的認(rèn)證與授權(quán)
在ZooKeeper server端, 每個(gè)znode存儲(chǔ)兩部分內(nèi)容: 數(shù)據(jù)和狀態(tài). 狀態(tài)中包含ACL信息. 創(chuàng)建一個(gè)znode會(huì)產(chǎn)生一個(gè)ACL表, 每個(gè)ACL記錄有以下內(nèi)容:
ZooKeeper提供了如下幾種驗(yàn)證模式(scheme)
注意的是, exists操作的getAcl操作不受ACL控制, 任何client都可以執(zhí)行這兩個(gè)操作.
znode的權(quán)限主要有以下幾種:
| zookeeper.DigestAuthenticationProvider.superDigest | 僅能通過(guò)Java系統(tǒng)屬性設(shè)置 | 3.2版本中的新配置項(xiàng). 允許管理員以超級(jí)用戶的身份來(lái)訪問(wèn)ZooKeeper中的層級(jí)名稱空間. 當(dāng)以超級(jí)用戶訪問(wèn)時(shí), znode的ACL完全放行. 以參數(shù)"super:<password>"來(lái)調(diào)用org.apache.zookeeper.server.auth.DigestAuthenticationProvider可以生成一個(gè)超級(jí)用戶. 然后用前面命令生成的"super:<data>"作為Java系統(tǒng)屬性, 在啟動(dòng)各個(gè)server的時(shí)候傳遞給進(jìn)程, 就開(kāi)啟了這個(gè)功能. 注意當(dāng)一個(gè)client通過(guò)scheme=digest的方式來(lái)認(rèn)證, 并且提供的認(rèn)證數(shù)據(jù)是超級(jí)用戶的認(rèn)證數(shù)據(jù), 也就是前面提到的"super:"`的時(shí)候, 就可以對(duì)server為所欲為了. 注意啊, 認(rèn)證信息從client傳遞給server的過(guò)程中, 是明文傳輸?shù)? 所以沒(méi)事不要作死. |
實(shí)驗(yàn)性的配置項(xiàng)
| Read Only Mode Server | readonlymode.enabled | 顯然這個(gè)值只能有過(guò)Java系統(tǒng)屬性來(lái)設(shè)置. 將其設(shè)置為true, 配置ZooKeeper為只讀模式. 具體細(xì)節(jié)參見(jiàn)ZOOKEEPER-784 |
不安全的配置項(xiàng)
| forceSync | zookeeper.forceSync | 默認(rèn)情況下, ZooKeeper強(qiáng)制要求在有數(shù)據(jù)變更請(qǐng)求時(shí), 先寫事務(wù)日志, 再執(zhí)行變更. 如果將這個(gè)配置設(shè)置為no, ZooKeeper在執(zhí)行數(shù)據(jù)變更時(shí)就不會(huì)等事務(wù)日志寫完再執(zhí)行了. |
| jute.maxbuffer | jute.maxbuffer | 這個(gè)配置項(xiàng)僅能通過(guò) Java系統(tǒng)屬性設(shè)置. 這設(shè)置的是一個(gè)znode中能存儲(chǔ)的數(shù)據(jù)的容量. 默認(rèn)值是0xfffff, 也就是1M. 注意如果要更改這個(gè)值, 所有server都要同步更改. 注意如果你要改這個(gè)值, 請(qǐng)先反思一下你使用ZooKeeper的姿勢(shì)是不是有點(diǎn)不正確 |
| skipACL | zookeeper.skipACL | 跳過(guò)ACL檢查, 這能帶來(lái)極大的性能提升, 但很不安全 |
| quorumListenOnAllIPs | 無(wú) | 如果設(shè)置為true, server在監(jiān)聽(tīng)端口的時(shí)候, 將嘗試監(jiān)聽(tīng)所有本地可用的IP+端口的組合. 默認(rèn)情況下這個(gè)配置是false, 這個(gè)行為是關(guān)閉的 |
使用Netty框架進(jìn)行通信
這是3.4版本后的一個(gè)特性. Netty是一個(gè)基于NIO的客戶端-服務(wù)器通信框架, 這個(gè)框架簡(jiǎn)化了Java在網(wǎng)絡(luò)通信層上的很多繁操作, 并且內(nèi)置支持SSL和認(rèn)證授權(quán), 當(dāng)然SSL和認(rèn)證授權(quán)是額外的可選功能.
3.4之前, ZooKeeper是直接用NIO的, 在3.4之后, NIO只是一個(gè)可選項(xiàng), 但依然是默認(rèn)選項(xiàng), 如果要使用Netty的話, 需要把zookeeper.serverCnxnFactory替換為org.apache.zookeeper.server.NettyServerCnxnFactory. 你可以只在client上使用Netty, 也可以在server上使用Netty, 但通常情況下, 建議要改一起改.
蛋疼的是相關(guān)的文檔官方還沒(méi)有寫.
4.1.2.11 四字命令
ZooKeeper支持一系列的四字命令, 你可以在client上通過(guò)telnte或nc直接向server發(fā)送這些四字命令.
使用一個(gè)四字命令如下所示, 下面使用echo和nc將四字命令ruok發(fā)送給本機(jī)的server
$ echo ruok | nc 127.0.0.1 5111下表是所有支持的四字命令, 注意有些命令僅在特定版本之后才受支持:
| conf | 3.3版本后支持. 打印出server的配置信息 |
| cons | 3.3版本后支持. 打印出server上的所有連接/會(huì)話 |
| crst | 3.3版本后支持. 重置有關(guān)連接/會(huì)話的統(tǒng)計(jì)數(shù)據(jù) |
| dump | 這個(gè)命令只有發(fā)給ensemble中的爹有才效. 列出所有重要的會(huì)話和生命周期隨會(huì)話的znode |
| envi | 打印出server運(yùn)行環(huán)境 |
| ruok | are you ok, 如果回復(fù) imok, 則說(shuō)明這個(gè)server很健康. 如果server有問(wèn)題, 則不會(huì)收到回復(fù). 需要注意的是, 一個(gè)server回復(fù)了ruok不代表這個(gè)server在ensemble中的狀態(tài)是正常的, 這僅代表server進(jìn)程正常啟動(dòng)了. 要查看ensemble的概況需要用stat命令 |
| srst | 重置server上的所有統(tǒng)計(jì)數(shù)據(jù) |
| srvr | 3.3后的新命令. 列出server的全部信息 |
| stat | 列出server的細(xì)節(jié)信息和與之相連的clients |
| wchs | 3.3后的新命令, 列出對(duì)該server的所有監(jiān)控(watch) |
| wchc | 3.3后的新命令, 列出監(jiān)控這個(gè)server的所有會(huì)話, 并列出每個(gè)會(huì)話監(jiān)控的名稱空間路徑. 注意, 在會(huì)話較多的server上, 這個(gè)命令可能會(huì)相當(dāng)耗時(shí) |
| wchp | 3.3后的新命令, 列出被監(jiān)控的所有層級(jí)名稱空間路徑, 以及相關(guān)的會(huì)話. 注意同上, 這個(gè)命令也可能會(huì)相當(dāng)耗時(shí) |
| mntr | 3.4后的新命令. 列出有關(guān)ensemble的一系列狀態(tài)值. 通過(guò)這些狀態(tài)值可以查看整個(gè)ensemble是不是正常 |
| isro | 3.4后的新命令. 檢查server是否運(yùn)行在只讀狀態(tài), 回復(fù)ro代表server在只讀狀態(tài), 回復(fù)rw代表server在可讀可寫狀態(tài) |
| gtmk | 獲取當(dāng)前的trace mask值, 以10進(jìn)制64位有符號(hào)數(shù)值形式返回, 具體trace mask的含義下面會(huì)講 |
| stmk | 設(shè)置trace mask |
這里需要注意的有:
| 0b0000000001 | 保留位 |
| 0b0000000010 | 記錄client的請(qǐng)求, 不包括ping |
| 0b0000000100 | 保留位 |
| 0b0000001000 | 記錄client的ping請(qǐng)求 |
| 0b0000010000 | 記錄來(lái)自爹的信息, 不包括ping |
| 0b0000100000 | 記錄會(huì)話的創(chuàng)建, 銷毀和核實(shí) |
| 0b0001000000 | 記錄監(jiān)控事件發(fā)生時(shí)向client報(bào)告的日志 |
| 0b0010000000 | 記錄來(lái)自爹的ping |
| 0b0100000000 | 保留位 |
| 0b1000000000 | 保留位 |
默認(rèn)的trace mask是0b0100110010. 另外stmk的用法也稍微復(fù)雜一點(diǎn), 還要注意將trace mask的值通過(guò)stmk命令傳遞給server的時(shí)候, 要使用大端字節(jié)序, 也就是網(wǎng)絡(luò)序. 下面是一個(gè)使用perl調(diào)用stmk命令的例子:
$ perl -e "print 'stmk', pack('q>', 0b0011111010)" | nc localhost 2181 250調(diào)用stmk命令時(shí), server會(huì)將設(shè)置后的trace mask以十進(jìn)制數(shù)值的形式返回回來(lái).
4.1.2.12 數(shù)據(jù)文件管理
將事務(wù)日志文件和快照文件存儲(chǔ)在不同的物理磁盤上, 可以提升系統(tǒng)性能.
快照存儲(chǔ)目錄
配置項(xiàng)dataDir指向的目錄路徑中主要存儲(chǔ)兩種文件:
server實(shí)例的編號(hào)用在兩個(gè)場(chǎng)合: myid文件里, 以及配置文件里的server.X配置項(xiàng)中. 當(dāng)前server實(shí)例在啟動(dòng)的時(shí)候, 先去配置文件里看dataDir的值, 然后去找dataDir/myid這個(gè)文件, 查看文件內(nèi)容, 得知自己的編號(hào), 然后在配置文件里再找對(duì)應(yīng)的server.x查看要開(kāi)的端口號(hào).
快照文件的后綴, <zxid>, 是一個(gè)事務(wù)ID. 這是在落地內(nèi)存數(shù)據(jù)庫(kù)這個(gè)過(guò)程開(kāi)始時(shí), 成功執(zhí)行的最后一個(gè)事務(wù)的ID號(hào), 但蛋疼的是, 在落地快照的過(guò)程中, server還在接受請(qǐng)求, 執(zhí)行事務(wù), 也就是在落地的過(guò)程中, 內(nèi)存數(shù)據(jù)庫(kù)中的數(shù)據(jù)還處于一個(gè)變動(dòng)的過(guò)程中, 這就導(dǎo)致落地后的快照文件像是一個(gè)扭曲的文件. 像是你在用手機(jī)拍攝全景照片的過(guò)程中, 有一只貓隨著你的鏡頭走, 然后最終拍攝出來(lái)的照片里有一只長(zhǎng)度為17米的貓. 最終落地生成的快照文件里的數(shù)據(jù)狀態(tài)可能和任何一個(gè)時(shí)刻內(nèi)存數(shù)據(jù)庫(kù)的狀態(tài)都對(duì)不上, 就是因?yàn)檫@個(gè)原因. 但ZooKeeper依然可以用這種扭曲的快照文件重建內(nèi)存數(shù)據(jù)庫(kù), 這是因?yàn)閆ooKeeper中的update操作是冪等的, 這就保證了在扭曲的快照文件之上重放事務(wù)日志里的日志, 就可以將進(jìn)程的內(nèi)存狀態(tài)恢復(fù)到日志結(jié)束時(shí)的那個(gè)時(shí)刻.
事務(wù)日志目錄
在有update請(qǐng)求的時(shí)候, server的默認(rèn)行為是先寫事務(wù)日志, 再執(zhí)行update操作. 單個(gè)事務(wù)日志里存儲(chǔ)的事務(wù)個(gè)數(shù)超過(guò)一個(gè)閾值的時(shí)候, 就會(huì)導(dǎo)致事務(wù)日志新開(kāi)一個(gè)文件, 同時(shí)會(huì)導(dǎo)致內(nèi)在數(shù)據(jù)庫(kù)落地快照, 這個(gè)閾值在上面的配置項(xiàng)中有提. 日志文件的后綴是日志文件里第一個(gè)日志的ID
文件管理
快照文件的格式和事務(wù)日志文件的格式是死的, 這就允許你從現(xiàn)網(wǎng)的server機(jī)器上將事務(wù)日志和內(nèi)存快照拷貝至你的開(kāi)發(fā)機(jī), 在你的開(kāi)發(fā)環(huán)境重現(xiàn)現(xiàn)網(wǎng)的情景, 從而進(jìn)行一些調(diào)試或問(wèn)題定位操作.
使用舊的事務(wù)日志文件和快照文件還能重建過(guò)去某個(gè)指定時(shí)刻server的狀態(tài), LogFormatter類可以用來(lái)訪問(wèn)事務(wù)日志文件, 以獲取可讀的信息. 當(dāng)然使用的時(shí)候需要有管理員權(quán)限, 因?yàn)閿?shù)據(jù)是加密的.
server進(jìn)程本來(lái)是沒(méi)有刪除事務(wù)日志和快照文件的能力的, 但這在3.4版本中也隨著新的配置項(xiàng)autopurge.snapRetainCount和autopurge.purgeInterval添加上了.
4.1.2.13 要避免的事情
下面是幾個(gè)你應(yīng)當(dāng)在部署運(yùn)維的時(shí)候極力避免的事情:
轉(zhuǎn)載于:https://www.cnblogs.com/neooelric/p/9230967.html
總結(jié)
以上是生活随笔為你收集整理的ZooKeeper: 简介, 配置及运维指南的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: USACO-Section1.5 Ari
- 下一篇: 岭回归——减少过拟合问题