Chapter 10 執行 ZooKeeper

weixin_34253539發表於2018-12-11

一、配置 ZooKeeper 伺服器

ZooKeeper 伺服器在啟動時從一個名為 zoo.cfg 的配置檔案讀取所有選項,多個伺服器如果角色相似,同時基本配置資訊一樣,就可以共享一個檔案。
data 目錄下的 myid 檔案用於區分各個伺服器,對每個伺服器來說,data 目錄是唯一的,因此這個目錄可以更加方便地保持一些差異化檔案。伺服器ID 將 myid 檔案作為一個索引引入到配置檔案中,一個特定的 ZooKeeper 伺服器可以知道如何配置自己引數。
配置引數常常通過配置檔案的方式進行設定,本節後續部分,通過列表方式列出了這些引數。很多引數也可以通過Java的系統屬性傳遞,其形式通常為zookeeper.propertyName,在啟動伺服器時,通過-D選項設定這些屬性。不過,系統屬性所對應的一個特定引數對服務來說是插入的配置,配置檔案中的配置引數優先於系統屬性中的配置。

1、基本配置

  • clientPort
    客戶端所連線的伺服器所監聽的TCP埠,預設情況下,服務端會監聽在所有的網路連線介面的這個埠上,除非設定了clientPortAddress引數。客戶端埠可以設定為任何值,不同的伺服器也可以監聽在不同的埠上。預設埠號為2181。
  • dataDir 和 dataLogDir

    • dataDir:用於配置記憶體資料庫儲存的模糊快照的目錄,如果某個伺服器為叢集中的一臺,id檔案也儲存在該目錄下。dataDir並不需要配置到一個專用儲存裝置上,快照將會以後臺執行緒的方式寫入,且並不會鎖定資料庫,而且快照的寫入方式並不是同步方式,直到寫完整快照為止。
    • dataLogDir:事務日誌對該目錄所處的儲存裝置上的其他活動更加敏感,服務端會嘗試進行順序寫入事務日誌,以為服務端在確認一個事務前必須將資料同步到儲存中,該裝置的其他活動(尤其是快照的寫入)可能導致同步時磁碟過於忙碌,從而影響寫入的吞吐能力。因此,最佳實踐是使用專用的日誌儲存裝置,將dataLogDir的目錄配置指向該裝置。
  • tickTime
    tick的時長單位為毫秒,tick為ZooKeeper使用的基本的時間度量單位,在9.7節已經介紹過,該值還決定了會話超時的儲存器大小。Zookeeper叢集中使用的超時時間單位通過tickTime指定,也就說,實際上tickTime設定了超時時間的下限值,因為最小的超時時間為一個tick時間,客戶端最小會話超時事件為兩個tick時間。
    tickTime的預設值為3000毫秒,更低的tickTime值可以更快地發現超時問題,但也會導致更高的網路流量(心跳訊息)和更高CPU使用率(會話儲存器的處理)。

2、儲存配置

  • preAllocSize
    用於設定預分配的事務日誌檔案(zookeeper.preAllocSize)的大小值,以KB為單位。
    當寫入事務日誌檔案時,服務端每次會分配preAllocSize值的KB的儲存大小,通過這種方式可以分攤檔案系統將磁碟分配儲存空間和更新後設資料的開銷,更重要的是,該方式也減少了檔案定址操作的次數。

    預設情況下preAllocSize的值為64MB,縮小該值的一個原因是事務日誌永遠不會達到這麼大,因為每次快照後都會重新啟動一個新的事務日誌,如果每次快照之間的日誌數量很小,而且每個事務本身也很小,64MB的預設值顯然就太大了。例如,如果我們每1000個事務進行一次快照,每個事務的平均大小為100位元組,那麼100KB的preAllocSize值則更加合適。預設的preAllocSize值的設定適用於預設的snapCount值和平均事務超過512位元組的情況。

  • snapCount
    指定每次快照之間的事務數(zookeeper.snapCount)。
    當Zookeeper伺服器重啟後需要恢復其狀態,恢復時兩大時間因素分別是為恢復狀態而讀取快照的時間以及快照啟動後所發生的事務的執行時間。執行快照可以減少讀入快照檔案後需要應用的事務數量,但是進行快照時也會影響伺服器效能,即便是通過後臺執行緒的方式進行寫入操作。
    snapCount的預設值為100000,因為進行快照時會影響效能,所以叢集中所有伺服器最好不要在同一時間進行快照操作,只要仲裁伺服器不會一同進行快照,處理時間就不會受影響,因此每次快照中實際的事務數為一個接近snapCount值的隨機數。
    注意,如果snapCount數已經達到,但前一個快照正在進行中,新的快照將不會開始,伺服器也將繼續等到下一個snapCount數量的事務後再開啟一個新的快照。
  • autopurge.snapRetainCount
    當進行清理資料操作時,需要保留在快照數量和對應的事務日誌檔案數量。
    ZooKeeper將會定期對快照和事務日誌進行垃圾回收操作,autopurge.snapRetainCount值指定了垃圾回收時需要保留的快照數,顯然,並不是所有的快照都可以被刪除,因為那樣就不可能進行伺服器的恢復操作。autopurge.snapRetainCount的最小值為3,也是預設值的大小。
  • autopurge.purgeInterval
    對快照和日誌進行垃圾回收(清理)操作的時間間隔的小時數。如果設定為一個非0的數字,autopurge.purgeInterval指定了垃圾回收週期的時間間隔,如果設定為0,預設情況下,垃圾回收不會自動執行,而需要通過ZooKeeper發行包中的zkCleanup.sh指令碼手動執行。
  • fsync.warningthresholdms
    觸發警告的儲存同步時間閥值(fsync.warningthresholdms),以毫秒為單位。
    ZooKeeper伺服器在應答變化訊息前會同步變化情況到儲存中。如果同步系統呼叫消耗了太長時間,系統效能就會受到嚴重影響,伺服器會跟蹤同步呼叫的持續時間,如果超過fsync.warningthresholdms只就會產生一個警告訊息。預設情況下,該值為1000毫秒。
  • weight.x=n
    該選項常常以一組引數進行配置,該選項指定組成一個仲裁機構的某個伺服器的權重為n,其權重n值指示了該伺服器在進行投票時的權重值。在ZooKeeper中一些部件需要投票值,比如群首選舉中和原子廣播協議中。預設情況下,一個伺服器的權重值為1,如果定義的一組伺服器沒有指定權重,所有伺服器的權重值將預設分配為1。
  • traceFile
    持續跟蹤ZooKeeper的操作,並將操作記錄到跟蹤日誌中,跟蹤日誌的檔名為traceFile.year.month.day。除非設定了該選項(requestTraceFile),否則跟蹤功能將不會啟用。
    該選項用來提供ZooKeeper所進行的操作的詳細檢視。不過,要想記錄這些日誌,ZooKeeper伺服器必須序列化操作,並將操作寫入磁碟,這將爭用CPU和磁碟的時間。如果你使用了該選項,請確保不要將跟蹤檔案放到日誌檔案的儲存裝置中。還需要知道,跟蹤選項還可能影響系統執行,甚至可能會很難重現跟蹤選項關閉時發生的問題。另外還有個有趣的問題,traceFile選項的Java系統屬性配置中不含有zookeeper字首,而且系統屬性的名稱也與配置選項名稱不同,這一點請小心。

3、網路配置

這些配置引數可以限制伺服器和客戶端之間的通訊,超時選項也在該節進行討論:
  • globalOutstandingLimit
    ZooKeeper中待處理請求的最大值(zookeeper.globalOutstandingLimit)。
    ZooKeeper客戶端提交請求比ZooKeeper服務端處理請求要快很多,服務端將會對接收到的請求佇列化,最終(也許幾秒之內)可能導致服務端的記憶體溢位。為了防止發生這個問題,ZooKeeper服務端中如果待處理請求達到globalOutstandingLimit值就會限制客戶端的請求。但globalOutstandingLimit值並不是硬限制,因為每個客戶端至少有一個待處理請求,否則會導致客戶端超時,因此,當達到globalOutstandingLimit值後,服務端還會繼續接收客戶端連線中的請求,條件是這個客戶端在伺服器中沒有任何待處理的請求。
    為了確定某個伺服器的全侷限制值,我們只是簡單地將該引數值除以伺服器的數量,目前還沒有更智慧的方式去實現全域性待處理運算元量的計算,並強制採用該引數所指定的限制值,因此,該限制值為待處理請求的上限值,事實上,伺服器之間完美的負載均衡解決方案還無法實現,所以某些伺服器執行得稍緩慢一點,或者處於更高的負載中,即使最終沒有達到全侷限制值也可能被限制住吞吐量。
    該引數的預設值為1000個請求,你可能並不會修改該引數值,但如果你有很多客戶端傳送大資料包請求可能就需要降低這個引數值,但我們在實踐中還未遇到需要修改這個引數的情況。
  • maxClientCnxns
    允許每個IP地址的併發socket連線的最大數量。Zookeeper通過流量控制和限制值來避免過載情況的發生。一個連線的建立所使用的資源遠遠高於正常操作請求所使用的資源。我們曾看到過某些錯誤的客戶端每秒建立很多ZooKeeper連線,最後導致拒絕服務(DoS),為了解決這個問題,我們新增了這個選項,通過設定該值,可以在某個IP地址已經有maxClientCnxns個連線時拒絕該IP地址新的連線。該選項的預設值為60個併發連線。
    注意,每個伺服器維護著這個連線的數量,如果有一個5個伺服器的叢集,並且使用預設的併發連線數60,一個欺詐性的客戶端會隨機連線到這5個不同的伺服器,正常情況下,該客戶端幾乎可以從單個IP地址上建立300個連線,之後才會觸發某個伺服器的限制。
  • clientPortAddress
    限制客戶端連線到指定的接收資訊的地址上。預設情況下,一個ZooKeeper伺服器會監聽在所有的網路介面地址上等待客戶端的連線。
    有些伺服器配置了多個網路介面,其中一個網路介面用於內網通訊,另一個網路介面用於公網通訊,如果你並不希望伺服器在公網介面接受客戶端的連線,只需要設定clientPortAddress選項為內網介面的地址。
  • minSessionTimeout
    最小會話超時時間,單位為毫秒。當客戶端建立一個連線後就會請求一個明確的超時值,而客戶端實際獲得的超時值不會低於minSessionTimeout的值。
    minSessionTimeout的預設值為tickTime值的兩倍。配置該引數值過低可能會導致錯誤的客戶端故障檢測,配置該引數值過高會延遲客戶端故障的檢測時間。
  • maxSessionTimeout
    會話的最大超時時間值,單位為毫秒。當客戶端建立一個連線後就會請求一個明確的超時值,而客戶端實際獲得的超時值不會高於maxSessionTimeout的值。
    雖然該引數並不會影響系統的效能,但卻可以限制一個客戶端消耗系統資源的時間,預設情況下maxSessionTimeout的時間為tickTime的20倍。

4、叢集配置

當以一個叢集來構建ZooKeeper服務時,需要為每臺伺服器配置正確的時間和伺服器列表資訊,以便伺服器之間可以互相建立連線並進行故障監測,在ZooKeeper的叢集中,這些引數的配置必須一致:
  • initLimit
    對於追隨者最初連線到群首時的超時值,單位為tick值的倍數。
    當某個追隨者最初與群首建立連線時,它們之間會傳輸相當多的資料,尤其是追隨者落後整體很多時。配置initLimit引數值取決於群首與追隨者之間的網路傳輸速度情況,以及傳輸的資料量大小,如果ZooKeeper中儲存的資料量特別大(即存在大量的znode節點或大資料集)或者網路非常緩慢,就需要增大initLimit值,因為該值取決於環境問題,所有沒有預設值。需要為該引數配置適當的值,以便可以傳輸所期望的最大快照,也許有時你需要多次傳輸,你可以配置initLimit值為兩倍你所期望的值。如果配置initLimit值過高,那麼首次連線到故障的伺服器就會消耗更多的時間,同時還會消耗更多的恢復時間,因此最好在你的網路中進行追隨者與群首之間的網路基準測試,以你規劃所使用的資料量來測試出你所期望的時間。
  • syncLimit
    對於追隨者與群首進行sync操作時的超時值,單位為tick值的倍數。
    追隨者總是會稍稍落後於群首,但是如果因為伺服器負載或網路問題,就會導致追隨者落後群首太多,甚至需要放棄該追隨者,如果群首與追隨者無法進行sync操作,而且超過了syncLimit的tick時間,就會放棄該追隨者。與initLimit引數類似,syncLimit也沒有預設值,與initLimit不同的是,syncLimit並不依賴於ZooKeeper中儲存的資料量大小,而是依賴於網路的延遲和吞吐量。在高延遲網路環境中,傳送資料和接收響應包會耗費更多時間,此時就需要調高syncLimit值。即使在相對低延遲的網路中,如果某些相對較大的事務傳輸給追隨者需要一定的時間,你也需要提高syncLimit值。
  • leaderServes
    配置值為“yes”或“no”標誌,指示群首伺服器是否為客戶端提供服務(zookeeper.leaderServes)。
    擔任群首的ZooKeeper伺服器需要做很多工作,它需要與所有追隨者進行通訊並會執行所有的變更操作,也就意味著群首的負載會比追隨者的負載高,如果群首過載,整個系統可能都會受到影響。
    該標誌位如果設定為“no”就可以使群首除去服務客戶端連線的負擔,使群首將所有資源用於處理追隨者傳送給它的變更操作請求,這樣可以提高系統狀態變更操作的吞吐能力。換句話說,如果群首不處理任何與其直連的客戶端連線,追隨者就會有更多的客戶端,因為連線到群首的客戶端將會分散到追隨者上,尤其注意在叢集中伺服器數量比較少的時候。預設情況下,leaderServes的值為“yes”。
  • server.x=[hostname]:n:n[:observer]
    伺服器x的配置引數。
    ZooKeeper伺服器需要知道它們如何通訊,配置檔案中該形式的配置項就指定了伺服器x的配置資訊,其中x為伺服器的ID值(一個整數)。當一個伺服器啟動後,就會讀取data目錄下myid檔案中的值,之後伺服器就會使用這個值作為查詢server.x項,通過該項中的資料配置伺服器自己。如果需要連線到另一個伺服器y,就會使用server.y項的配置資訊來與這個伺服器進行通訊。
    其中hostname為伺服器在網路n中的名稱,同時後面跟了兩個TCP的埠號,第一個埠用於事務的傳送,第二個埠用於群首選舉,典型的埠號配置為2888:3888。如果最後一個欄位標記了observer屬性,伺服器就會進入觀察者模式。
    注意,所有的伺服器使用相同的server.x配置資訊,這一點非常重要,否則的話,因伺服器之間可能無法正確建立連線而導致整個叢集無法正常工作。
  • cnxTimeout
    在群首選舉開啟一個新的連線的超時值(zookeeper.cnxTimeout)。
    ZooKeeper伺服器在進行群首選舉時互相之間會建立連線,該選項值確定了一個伺服器在進行重試前會等待連線成功建立的時間為多久,9.2節介紹了該超時的用途。預設的超時時間為5秒,該值足夠大,也許你並不需要修改。
  • electionAlg
    選舉演算法的配置選項。
    為了整個配置的完整性,我們也列入了該選項。該選項用於選擇不同的群首選舉演算法,但除了預設的配置外,其他演算法都已經棄用了,所以並不需要配置這個選項。

5、認證和授權選項

該節中包括認證和授權相關的選型配置。對於Kerberos相關的配置選項資訊,請參考6.1.2節:
zookeeper.DigestAuthenticationProvider.superDigest(只適用於Java系統屬性)該系統屬性指定了“super”使用者的密碼摘要資訊(該功能預設不啟用),以super使用者認證的客戶端會跳過所有ACL檢查。該系統屬性的值形式為super:encoded_digest。為了生成加密的摘要,可以使用org.apache.zookeeper.server.auth.DigestAuthenticationProvider工具,使用方式如下:
java -cp $ZK_CLASSPATH \ org.apache.zookeeper.server.auth.DigestAuthenticationProvider super:asdf

通過命令列工具生成一個 asdf 密碼的加密摘要資訊:
super:asdf->super:T+4Qoey4ZZ8Fnni1Yl2GZtbH2W4=

為了在伺服器啟動中使用該摘要,可以通過以下命令實現:
export SERVER_JVMFLAGS
SERVER_JVMFLAGS=-Dzookeeper.DigestAuthenticationProvicder.superDigest=
    suer:T+4Qoey4ZZ8Fnni1Yl2GZtbH2W4=
./bin/zkServer.sh start

現在通過 zkCli 進行連線:
[zk: localhost:2181(CONNECTED) 0] addauth digest super:asdf
[zk: localhost:2181(CONNECTED) 1]
此時,已經以super使用者的身份被認證,現在不會被任何ACL所限制。

【注意:不安全連線】

ZooKeeper客戶端與伺服器之間的連線並未加密,因此不要在不可信的連結中使用super的密碼,使用super密碼的安全方式是在ZooKeeper伺服器本機上使用super密碼執行客戶端。

6、非安全配置

  • forceSync
    通過“yes”或“no”選項可以控制是否將資料資訊同步到儲存裝置上(zookeeper.forceSync)。
    預設情況下,forceSync配置yes時,事務只有在同步到儲存裝置後才會被應答,同步系統呼叫的消耗很大,而且也是事務處理中最大的延遲原因之一。如果forceSync配置為no,事務會在寫入到作業系統後就立刻被應答,在將事務寫入磁碟之前,這些事務常常快取於記憶體之中,配置forceSync為no可以提高效能,但代價是伺服器崩潰或停電故障時可恢復性。
  • jute.maxbuffer(僅適用於Java系統屬性)
    一個請求或響應的最大值,以位元組為單位。該選項只能通過Java的系統屬性進行配置,並且選項名稱沒有zookeeper字首。
    ZooKeeper中內建了一些健康檢查,其中之一就是對可傳輸的znode節點資料的大小的檢查,ZooKeeper被設計用於儲存配置資料,配置資料一般由少量的後設資料資訊(大約幾百位元組)所組成。預設情況下,一個請求或響應訊息如果大於1M位元組,就會被系統拒絕,可以使用該屬性來修改健康檢查值,調小檢查值,或者真的確認要調大檢查值。

【注意:修改健康檢查值】
雖然通過jute.maxbuffer指定的限制值可以進行大塊資料的寫入操作,但獲取一個znode節點的子節點,而同時該節點有很多子節點時就會出現問題。如果一個znode節點含有幾十萬個子節點,每個子節點的名字長度平均為10個字元,在試著返回子節點列表時就會命中預設最大緩衝大小檢查,此時就會導致連線被重置。

  • skipACL
    跳過所有ACL檢查(zookeeper.skipACL)。
    處理ACL檢查會有一定的開銷,通過該選項可以關閉ACL檢查功能,這樣做可以提高效能,但也會將資料完全暴露給任何一個可以連線到伺服器的客戶端。
  • readonlymode.enabled(僅適用於Java系統屬性)
    將該配置設定為true可以啟用伺服器只讀模式功能,客戶端可以以只讀模式的請求連線伺服器並讀取資訊(可能是已過期的資訊),即使該伺服器在仲裁中因分割槽問題而被分隔。為了啟用只讀模式,客戶端需要配置canBeReadOnly為true。
    該功能可以使客戶端即使在網路分割槽發生時也能讀取(不能寫入)ZooKeeper的狀態,在這種情況下,被分割槽而分離的客戶端依然可以繼續取得進展,並不需要等待分割槽問題被修復。特別注意,一個與叢集中其他伺服器失去連線ZooKeeper也許會終止以只讀模式提供過期的資料服務。

7、日誌

ZooKeeper採用SLF4J庫(JAVA簡易日誌門面)作為日誌的抽象層,預設使用Log4J進行實際的日誌記錄功能。
Log4J的配置檔案為log4j.properties,系統會從classpath中載入這個檔案,對於Log4J比較失望的是,如果對應路徑不存在log4j.properties檔案,我們會看到以下輸出資訊:
log4j:WARN No appenders could be found for logger (org.apache.zookeeper.serv ...
log4j:WARN Please initialize the log4j system properly.

一般,log4j.properties會儲存到classpath中的conf目錄下,在ZooKeeper中的log4j.properties檔案的主要部分:
zookeeper.root.logger=INFO, CONSOLE ①
zookeeper.console.threshold=INFO
zookeeper.log.dir=.
zookeeper.log.file=zookeeper.log
zookeeper.log.threshold=DEBUG
zookeeper.tracelog.dir=.
zookeeper.tracelog.file=zookeeper_trace.log
log4j.rootLogger=${zookeeper.root.logger} ②
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender ③
log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold} ④
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout ⑤
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] -
...
log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender ⑥
log4j.appender.ROLLINGFILE.Threshold=${zookeeper.log.threshold} ⑦
log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file}
log4j.appender.ROLLINGFILE.MaxFileSize=10MB
log4j.appender.ROLLINGFILE.MaxBackupIndex=10
log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] -
①第一組配置中,所有配置項均以zookeeper.開頭,配置了該檔案的預設值,這些配置項實際上是系統屬性配置,可以通過java命令列指定-D引數來覆蓋JVM的配置。第一行的日誌配置中,預設配置了日誌訊息的級別為INFO,即所有低於INFO級別的日誌訊息都會被丟棄,使用的appender為CONSOLE。你可以指定多個appender,例如,你如果想將日誌資訊同時輸出到CONSOLE和ROLLINGFILE時,可以配置zookeeper.root.logger項為INFO,CONSOLE,ROLLINGFILE。
②rootLogger指定了處理所有日誌訊息的日誌處理器,因為我們並不需要其他日誌處理器。
③該行配置以CONSOLE名稱定義了一個類,該類會處理訊息的輸出。在這裡使用的是ConsoleAppender類。
④在appender的定義中也可以過濾訊息,該行配置了這個appender會忽略所有低於INFO級別的訊息,因為zookeeper.root.logger中定義了全域性閥值為INFO。
⑤appender使用的佈局類對輸出日誌在輸出前進行格式化操作。我們通過佈局模式定義了輸出日誌訊息外還輸出日誌級別、時間、執行緒資訊和呼叫位置等資訊。
⑥RollingFileAppender實現了滾動日誌檔案的輸出,而不是不斷地輸出到一個單個日誌檔案或控制檯。除非ROLLINGFILE被rootLogger引用,否則該appender會被忽略。
⑦定義ROLLINGFILE的輸出級別為DEBUG,因為rootLogger過濾了所有低於INFO級別的日誌,所以,你如果你想看DEBUG訊息,就必須將zookeeper.root.logger項的配置從INFO修改為DEBUG。

8、專用資源

當考慮在伺服器上執行ZooKeeper如何配置時,伺服器本身的配置也很重要。為了達到所期望的效能,可以考慮使用專用的日誌儲存裝置,就是說日誌目錄處於專屬的硬碟上,沒有其他程式使用該硬碟資源,甚至週期性的模糊快照也不會使用該硬碟。

二、配置 ZooKeeper 叢集

仲裁(quorum)的概念:該概念深深貫穿於ZooKeeper的設計之中。在複製模式下處理請求時以及選舉群首時都與仲裁的概念有關,如果ZooKeeper叢集中存在法定人數的伺服器已經啟動,整個叢集就可以繼續工作。
觀察者(observer):與叢集一同工作,接收客戶端請求並處理伺服器上的狀態變更,但是群首並不會等待觀察者處理請求的響應包,同時叢集在進行群首選舉時也不會考慮觀察者的通知訊息。

1、多數原則

當叢集中擁有足夠的 ZooKeeper 伺服器來處理請求時,稱這組伺服器的集合為仲裁法定人數。
當配置多個伺服器來組成ZooKeeper叢集時,我們預設使用多數原則作為仲裁法定人數。ZooKeeper會自動監測是否執行於複製模式,從配置檔案讀取時確定是否擁有多個伺服器的配置資訊,並預設使用多數原則的仲裁法定人數。

2、法定人數的可配置性

關於法定人數的一個重要屬性:
如果一個法定人數解散了,叢集中另一個法定人數形成,這兩個法定人數中至少有一個伺服器必須交集。
ZooKeeper也允許靈活的法定人數配置,這種特殊方案就是對伺服器進行分組配置時,會將伺服器分組成不相交的集合並分配伺服器的權重,通過這種方案來組成法定人數,需要使多陣列中的伺服器形成多數投票原則。
例如,有三個組,每個組中有三個伺服器,每個伺服器的權重值為1,在這種情況下,需要四個伺服器來組成法定人數:某個組中的兩個伺服器,另一組中的兩臺伺服器。
總之,其數學邏輯歸結為:如果有G個組,所需要的伺服器為一個G組的伺服器,滿足|G|>|G|/2,同時對於G組中的伺服器集合g,還需要集合g中的集合g滿足集合g的所有權重值之和W'不小於集合g的權重值之和(如:W>W/2)。
通過以下配置選項可以建立一個組:
group.x=n[:n]
啟用法定人數的分層構建方式。x為組的識別符號,等號後面的數字對應伺服器的識別符號,賦值操作符右側為冒號分隔的伺服器識別符號的列表。注意,組與組之間不能存在交集,所有組的並集組成了整個ZooKeeper叢集,換句話說,叢集中的每個伺服器必須在某個組中被列出一次。

【下面的示例說明了9個伺服器被分為3組的情況:】

group.1=1:2:3
group.2=4:5:6
group.3=7:8:9
這個例子中,每個伺服器的權重都一樣,為了構成法定人數,需要兩個組及這兩個組中各取兩個伺服器,也就是總共4個伺服器。但根據法定人數多數原則,至少需要5個伺服器來構成一個法定人數。注意,不能從任何子集形成法定人數的4個伺服器,不過,一個組全部伺服器加上另一個組的一個單獨的伺服器並不能構成法定人數。
當在跨多個資料中心部署ZooKeeper服務時,這種配置方式有很多優點。例如,一個組可能表示執行於不同資料中心的一組伺服器,即使這個資料中心崩潰,ZooKeeper服務也可以繼續提供服務。
在跨三個資料中心部署的這種方式可以容忍某個資料中心的故障問題,可以在兩個資料中心中每一個部署三個伺服器,而只在第三個資料中心部署一個伺服器,通過這種方式來使用多數原則,這樣,如果某個資料中心不可用,其他兩個資料中心還能組成法定人數。這種配置方式的優點是這七個伺服器中的任何四個都構成一個法定人數,而缺點是一旦某個資料中心不可用,其他資料中心中任何伺服器的崩潰都無法容忍。
如果只有兩個資料中心可用,可以使用【權重值】來表示優先權,例如,基於每個資料中心中的客戶端數量來配置權重值。只有兩個資料中心時,如果每個伺服器的權重值都一樣,就無法容忍任何一個資料中心的失效,但是如果對某個伺服器分配了更高的權重值,就可以容忍這兩個資料中心中某個資料中心的失效。假設,在每個資料中心中分配三個伺服器,並且將這些伺服器均放到同一組中:
group.1=1:2:3:4:5:6
因為所有的伺服器預設情況下權重值都一樣,因此只要6個伺服器中有4個伺服器有效時就可以構成法定人數的伺服器。當然,這也意味著如果某個資料中心失效,就不能形成法定人數,即使另一個資料中心的三個伺服器均有效。

【為了給伺服器分配不同的權重值,可以通過以下選型進行配置:】

weight.x=n
與group選項一起配合使用,通過該選項可以為某個伺服器形成法定人數時分配一個權重值為n。其值n為伺服器投票時的權重,ZooKeeper中群首選舉和原子廣播協議中均需要投票。預設情況下,伺服器的權重值為1,如果配置檔案中定義了組的選項,但為指定權重值,所有的伺服器均會被分配權重值1。

假設,某個資料中心只要其所有伺服器均可用,即使在其他資料中心失效時,這個資料中心也可以提供服務,我們暫且稱該資料中心為D1,此時,可以為D1中的某個伺服器分配更高權重值,以便可以更容易與其他伺服器組成法定人數。

【假設D1中有伺服器1、2和3,通過以下方式為伺服器1分配更高的權重值:】

weight.1=2
通過以上配置,就有了7個投票,在構成法定人數時,只需要4個投票。如果沒有weight.1=2引數,任何伺服器都需要與其他三個伺服器來構成法定人數,但有了這個引數配置,伺服器1與兩個伺服器就可以構成法定人數。因此,只要D1可用,即使其他資料中心發生故障,伺服器1、2和3也能構成法定人數並繼續提供服務。

通過以上不同的法定人數配置的若干示例,看到該配置對部署的影響。提供的分層方案非常靈活,通過不同的權重值和組的管理可以提供不同的分層配置。

3、觀察者

觀察者(observer)為ZooKeeper伺服器中不參與投票但保障狀態更新順序的特殊伺服器。

【配置ZooKeeper叢集使用觀察者,需要在觀察者伺服器的配置檔案中新增以下行】:

peerType=observer

【同時,還需要在所有伺服器的配置檔案中新增該伺服器的:observer定義。如下】:

server.1:localhost:2181:3181:observer

三、重配置

圖10-1的場景,三個伺服器(A、B、C)組成了整個叢集,伺服器C因某些網路擁塞問題稍稍落後於整個叢集,因此伺服器C剛剛瞭解事務到<1,3>(其中1為時間戳,3為對應該時間戳的事務標識,但因為伺服器A和B的通訊良好,所以伺服器C稍稍落後並不會導致整個系統變慢,伺服器A和B可以提交事務到<1,6>)。

【圖10-1:含有3個伺服器的叢集將要擴充套件到5個】
clipboard.png

現在,假設將所有服務停止,新增伺服器D和E到叢集中,當然這兩個新的伺服器並不存在任何狀態資訊,重新配置了伺服器A、B、C、D、E成為更大的叢集並啟動叢集恢復服務,因為現在有了五個伺服器,至少需要三個伺服器組成一個法定人數,而伺服器C、D、E足夠構成法定人數,因此在圖10-2中看到當這些伺服器構成法定人數並開始同步時都發生了什麼。這個場景可以簡單重現,如果伺服器A和B的啟動慢一些,比如伺服器A和B比其他三個伺服器的啟動晚一些。一旦新的法定人數開始同步,伺服器A和B就會與伺服器C進行同步,因為法定人數中伺服器C的狀態為最新狀態,法定人數的三個成員伺服器會同步到最後的事務<1,3>,而不會同步<1,4>、<1,5>和<1,6>這三個事務,因為伺服器A和B並未構成法定人數的成員。

【圖10-2:5個伺服器的叢集的法定人數為3】
clipboard.png

因為已經構成一個活躍的法定人數,這些伺服器可以開始提交新的事務,假設有兩個事務:<2,1>和<2,2>,如圖10-3所示,當伺服器A和B啟動後連線到伺服器C後,伺服器C作為群首歡迎其加入到叢集之中,並在收到事務<2,1>和<2,2>後立即告知伺服器A和B刪除事務<1,4>、<1,5>和<1,6>。
圖10-3:5個伺服器的叢集丟失資料
clipboard.png

這個結果非常糟糕,丟失了某些狀態資訊,而且狀態副本與客戶端所看到的<1,4>、<1,5>、<1,6>也不再一致。為了避免這個問題,ZooKeeper提供了重配置操作,這意味著運維人員並不需要手工進行重配置操作而導致狀態資訊的破壞,而且,也不需要停止任何服務。

【重配置】不僅可以改變叢集成員配置,還可以修改網路引數配
置,因為ZooKeeper中配置資訊的變化,需要將重配置引數與靜態的配置檔案分離,單獨儲存為一個配置檔案並自動更新該檔案。dynamicConfigFile引數和連結這兩個配置檔案。

【使用動態配置之前,回顧一下之前的配置檔案:】

tickTime=2000
initLimit=10
syncLimit=5
dataDir=./data
dataLogDir=./txnlog
clientPort=2182
server.1=127.0.0.1:2222:2223
server.2=127.0.0.1:3333:3334
server.3=127.0.0.1:4444:4445

【現在,將配置檔案修改為動態配置方式:】

tickTime=2000
initLimit=10
syncLimit=5
dataDir=./data
dataLogDir=./txnlog
dynamicConfigFile=./dyn.cfg

【注意】,甚至從配置檔案中刪除了clientPort引數配置,在dyn.cfg檔案由伺服器項的配置組成,同時還多了一些配置,伺服器項的配置形式如下:

server.id=host:n:n[:role];[client_address:]client_port

與正常的配置檔案一樣,列出了每個伺服器的主機名和埠號用於法定人數和群首選舉訊息。

  • role選項:必須為participant或observer,如果忽略role選項,預設為participant
  • client_port:還指定了client_port(用於客戶端連線的伺服器埠號),以及該伺服器需要繫結的特定網路介面地址,因為從靜態配置檔案中刪除了clientPort引數,所以在這裡新增該配置。

【最終的dyn.cfg配置檔案如下所示】:

server.1=127.0.0.1:2222:2223:participant;2181
server.2=127.0.0.1:3333:3334:participant;2182
server.3=127.0.0.1:4444:4445:participant;2183

使用重配置之前必須先建立這些檔案,一旦這些檔案就緒,就可以通過reconfig操作來重新配置一個叢集,該操作可以增量或全量(整體)地進行更新操作。

增量的重配置操作將會形成兩個列表:

  • 待刪除的伺服器列表
    待刪除的伺服器列表僅僅是一個逗號分隔伺服器ID列表
  • 待新增的伺服器項的列表
    待新增的伺服器項列表為逗號分隔的伺服器項列表,每個伺服器項的形式為動態配置檔案中所定義的形式。例如:
reconfig -remove 2,3 -add \
server.4=127.0.0.1:5555:5556:participant;2184,\
server.5=127.0.0.1:6666:6667:participant;2185

該命令將會刪除伺服器2和3,新增伺服器4和5。該操作成功執行還需要滿足某些條件:

  • 首先,與其他ZooKeeper操作一樣,原配置中法定人數必須處於活動狀態;
  • 其次,新的配置檔案中構成的法定人數也必須處於活動狀態。

【注意:通過重配置從一個伺服器到多個伺服器】

當只有一個單獨的ZooKeeper伺服器,該伺服器以獨立模式執行,這種情況稍微複雜一些,因為重配置不僅改變了法定人陣列成的元素,同時還會切換原來的伺服器模式從獨立模式到仲裁模式,所以,不允許以獨立模式執行重配置操作,只有在仲裁模式時才可以使用重配置功能。
ZooKeeper一次允許一個配置的變更操作請求,當然,配置操作會非常快地被處理,而且重新配置也很少發生,所以併發的重配置操作應該不是什麼問題。

還可以使用-file引數來指定一個新的成員配置檔案來進行一次全量更新。例如:reconfig-file newconf命令會產生如上面命令一樣的增量操作結果,newconf檔案為:

server.1=127.0.0.1:2222:2223:participant;2181
server.4=127.0.0.1:5555:5556:participant;2184
server.5=127.0.0.1:6666:6667:participant;2185
通過-members引數,後跟伺服器項的列表資訊,可以代替-file引數進行全量更新配置操作。
最後【-v】,所有形式的reconfig的為重新配置提供了條件,如果通過-v引數提供了配置版本號,reconfig命令會在執行前確認配置檔案當前的版本號是否匹配,只有匹配才會成功執行。可以通過讀取zookeeper/config節點來獲取當前配置的版本號,或通過zkCli工具來呼叫config獲取配置版本號資訊。

客戶端連線串的管理

客戶端也涉及一些相關的配置問題:連線串。
客戶端連線串常常表示為逗號分隔的host:port對,其中host為主機名或IP地址,通過主機名可以提供伺服器實際IP與所訪問的伺服器的識別符號之間的間接層的對應關係。
例如,運維人員可以替換ZooKeeper服務為另一個,而不需要改變客戶端的配置。
不過,該靈活性有一定限制,運維人員可以改變組成叢集的伺服器機器,但不能改變客戶端所使用的伺服器。
例如,如圖10-4所示,ZooKeeper可以通過重配置很簡單地將叢集從三個伺服器擴充套件到五個伺服器,但客戶端仍然使用三個伺服器,而不是五個。

【圖10-4:叢集從三個到五個伺服器時,客戶端的重配置】
clipboard.png

另一種方式可以使ZooKeeper的伺服器數量更具彈性,而不需要改變客戶端的配置。對主機名很自然地想到可以解析為一個IP地址,但實際上,一個主機名可以解析為多個地址,如果主機名解析為多個IP地址,ZooKeeper就可以連線到其中的任何地址,在圖10-4中,假設伺服器zk-a、zk-b和zk-c,解析為三個獨立的IP地址:10.0.0.1、10.0.0.2和10.0.0.3,現在假設通過DNS配置了一個單獨的主機名:zk,解析為這三個IP地址,只需要修改DNS的解析地址數量,之後啟動的任何客戶端都可以訪問這五個伺服器,如圖10-5所示。

【圖10-5:叢集從三個到五個伺服器時,使用DNS對客戶端的重配置】
clipboard.png

在使用主機名解析為多個地址方式時,還有一些注意事項:

  • 首先,所有的伺服器必須使用相同的客戶端埠號;
  • 其次,主機名解析只有在建立連線時才會發生,所以已經連線的客戶端無法知道最新的名稱解析,只能對新建立的ZooKeeper客戶端生效。
【路徑資訊】:
客戶端的連線還可以包含路徑資訊,該路徑指示瞭解析路徑名稱時的根路徑,其行為與UNIX系統中的chroot命令相似,而且在ZooKeeper社群中也會經常聽到人們以“chroot”來稱呼這個功能。
例如,如果客戶端的連線串為zk:2222/app/superApp,當客戶端連線並執行getData("/a.dat",...)操作時,實際客戶端會得到/app/superApp/a.dat節點的資料資訊(注意,連線串中指示的路徑必須存在,而不會建立連線串中所指示的路徑)。

在連線串中新增路徑資訊的動機在於一個ZooKeeper叢集為多個應用程式提供服務,這樣不需要要求每個應用程式新增其路徑的字首資訊。每個應用程式可以類似名稱獨享似的使用ZooKeeper叢集,運維人員可以按他們的期望來劃分名稱空間。
圖10-6的示例展示了不同的連線串可以為客戶端應用程式提供不同的根入口點。

【圖10-6:通過連線串指定ZooKeeper客戶端的根節點】
clipboard.png

【注意:連線串的重疊】
當管理客戶端連線串時,注意一個客戶端的連線串永遠不要包含兩個不同的ZooKeeper叢集的主機名,這是最快速也是最簡單導致腦裂問題的方式。

四、配額管理

ZooKeeper的另一個可配置項為配額,ZooKeeper初步提供了znode節點數量和節點資料大小的配額管理的支援。可以通過配置來指定某個子樹的配額,該子樹就會被跟蹤,如果該子樹超過了配額限制,就會記錄一條警告日誌,但操作請求還是可以繼續執行。此時,ZooKeeper會檢測是否超過了某個配額限制,但不會阻止處理流程。

配額管理的跟蹤功能通過/zookeeper子樹完成,所以應用程式不能在這個子樹中儲存自己的資料,這個子樹只應該保留給ZooKeeper使用,而 /zookeeper/quota 節點就是ZooKeeper管理配額的節點。為了對應用程式/application/superApp建立一個配額項,需要在application/superApp節點下建立兩個子節點zookeeper_limits和zookeeper_stats。

對於znode節點數量的限制稱之為count,而對於節點資料大小的限制則為bytes。在zookeeper_limits和zookeeper_stats節點中通過count=n,bytes=m來指定配額,其中n和m均為整數,在zookeeper_limits節點中,n和m表示將會觸發警告的級別(如果配置為-1就不會觸發警告資訊),在zookeeper_stats借點中,n和m分別表示當前子樹中的節點數量和子樹節點的資料資訊的當前大小。

【注意:對後設資料的配額跟蹤】

對於子樹節點資料的位元組數配額跟蹤功能,並不會包含每個znode節點的後設資料的開銷,後設資料的大小大約100位元組,所以如果每個節點的資料大小都比較小,跟蹤znode節點的數量比跟蹤znode資料的大小更加實用。

可以使用zkCli來建立/application/superApp節點,並配置配額限制:

[zk: localhost:2181(CONNECTED) 2] create /application ""
Created /application
[zk: localhost:2181(CONNECTED) 3] create /application/superApp super
Created /application/superApp
[zk: localhost:2181(CONNECTED) 4] setquota -b 10 /application/superApp
Comment: the parts are option -b val 10 path /application/superApp
[zk: localhost:2181(CONNECTED) 5] listquota /application/superApp
absolute path is /zookeeper/quota/application/superApp/zookeeper_limits
Output quota for /application/superApp count=-1,bytes=10
Output stat for /application/superApp count=1,bytes=5
建立了/application/superApp節點,且該節點的資料為5個位元組(一個單詞“super”),之後為/application/superApp節點設定了配額限制為10個位元組,當列出/application/superApp節點配置限制是,發現資料大小的配額還有5個位元組的餘量,而並未對這個子樹設定znode節點數量的配額限制,因為配額中count的值為-1。

如果傳送命令get/zookeeper/quota/application/superApp/zookeeper_stats,可以直接訪問該節點資料,而不需要使用zkCli工具,事實上,可以通過建立或刪除這些節點來建立或刪除配額配置。如果執行以下命令:

create /application/superApp/lotsOfData ThisIsALotOfData
就會在日誌中看到如下資訊:
Quota exceeded: /application/superApp bytes=21 limit=10

五、多租賃配置

配額,提供了配置選項中的某些限制措施,而ACL策略更值得我們考慮如何使用ZooKeeper來服務於多租賃(multitenancy)情況。滿足多租賃的一些令人信服的原因如下:

  • 為了提供可靠的伺服器,ZooKeeper伺服器需要執行於專用的硬體裝置之上,跨多個應用程式共享這些硬體裝置更容易符合資本投資的期望。
  • 發現,在大多數情況下,ZooKeeper的流量非常具有突發性:配置或狀態的變化的突發操作會導致大量的負載,從而導致服務長時間的不可用。如果是沒有什麼關聯的應用程式的突發操作,將這些應用程式共享這個伺服器更能有效利用硬體資源。不過還是要注意失聯事件發生時所產生的峰值,某些寫得不太規範的應用程式在處理Disconnected事件時,產生的負載高於其所需要的資源。
  • 對於硬體資源的分攤,可以獲得更好的故障容錯性:如果兩個應用程式,從之前各自三個伺服器的叢集中轉移到一個由5臺伺服器組成的叢集,總量上所使用的伺服器更少了,對ZooKeeper也可以容忍兩臺伺服器的故障,而不是之前的只能容忍一個伺服器故障。

當服務於多租賃的情況下時,運維人員一般會將資料樹分割為不同的子樹,每個子樹為某個應用程式所專用。開發人員在設計應用程式時可以考慮在其所用的znode節點前新增字首,但還有一個更簡單的方法來隔離各個應用程式:在連線串中指定路徑部分,10.3節中介紹了這方式。每個應用程式的開發人員在進行應用程式的開發時,就像使用專用的ZooKeeper服務一樣。如果運維人員決定將應用程式部署到根路徑/application/newapp之下,應用程式可以使用host:port/application/newapp連線串,而不僅僅是host:port,通過這種方式,對應用程式所呈現的猶如使用專用服務一樣,與此同時,運維人員還可以為/application/newapp節點配置配額限制,以便跟蹤應用程式的空間使用情況。

六、檔案系統佈局和格式

資料儲存有兩類:事務日誌檔案和快照檔案。
這些檔案均以普通檔案的形式儲存到本地檔案系統中,在進行關鍵路徑的事務處理時就會寫入事務日誌檔案,所以強烈建議將這些檔案儲存到一個專用儲存裝置上

快照檔案將會被寫入到DataDir引數所指定的目錄中,而事務日誌檔案將會被寫入到DataLogDir引數所指定的目錄中。

首先,看一下事務日誌目錄中的檔案,如果列出該目錄的資訊,會發現只有一個子目錄,名為version-2,對日誌和快照的格式只做出了一次重大改進,當改變其格式後,發現,將資料通過檔案版本進行分離,對於處理版本間的資料遷移是非常有用的。

1、事務日誌

在執行一些小測試後的目錄內容,有兩個事務日誌檔案:

-rw-r--r-- 1 breed 67108880 Jun 5 22:12 log.100000001
-rw-r--r-- 1 breed 67108880 Jul 15 21:37 log.200000001
可以仔細觀察這些檔案資訊。首先,考慮到測試很少,而這些檔案卻非常大(每個都超過6MB);其次這些檔名的字尾中均有一個很大數字。

ZooKeeper為檔案預分配大的資料塊,來避免每次寫入所帶來的檔案增長的後設資料管理開銷,如果通過對這些檔案進行十六進位制轉儲列印,會發現這些檔案中全部以null字元(0)填充,只有在最開始部分有少量的二進位制資料,伺服器執行一段時間後,其中的null字元逐漸被日誌資料替換。

日誌檔案中包含事務標籤zxid,但為了減輕恢復負載,而且為了快速查詢,每個日誌檔案的字尾為該日誌檔案中第一個zxid的十六進位制形式。通過十六進位制表示zxid的一個好處就是可以快速區分zxid中時間戳部分和計數器部分,所以在之前例子中的第一個檔案的時間戳為1,而第二個檔案的時間戳為2。

不過,還想繼續看一看檔案中儲存了什麼內容,對於問題診斷也非常有幫助。有時,開發人員宣稱ZooKeeper丟失了某些znode節點資訊,此時只有通過查詢事務日誌檔案才可以知道客戶端具體刪除過哪些節點。

可以通過一下命令來檢視第二個日誌檔案:
java -cp $ZK_LIBS org.apache.zookeeper.server.LogFormatter version-2 log.200000001
這個命令的輸出資訊如下:
7/15/13... session 0x13...00 cxid 0x0 zxid 0x200000001 createSession 30000
7/15/13... session 0x13...00 cxid 0x2 zxid 0x200000002 create
'/test,#22746573746 ...
7/15/13... session 0x13...00 cxid 0x3 zxid 0x200000003 create
'/test/c1,#6368696c ...
7/15/13... session 0x13...00 cxid 0x4 zxid 0x200000004 create
'/test/c2,#6368696c ...
7/15/13... session 0x13...00 cxid 0x5 zxid 0x200000005 create
'/test/c3,#6368696c ...
7/15/13... session 0x13...00 cxid 0x0 zxid 0x200000006 closeSession null
每個日誌檔案中的事務均以可讀形式一行行地展示出來。因為只有變更操作才會被記錄到事務日誌,所以在事務日誌中不會看到任何讀事務操作。

2、快照

快照檔案的命名規則與事務日誌檔案的命名規則相似,以下為之前例子中的伺服器的快照列表資訊:
-rw-r--r-- 1 br33d 296 Jun 5 07:49 snapshot.0
-rw-r--r-- 1 br33d 415 Jul 15 21:33 snapshot.100000009
快照檔案並不會被預分配空間,所以檔案大小也更加準確地反映了其中包含的資料大小。其中字尾表示快照開始時當時的zxid值,之前已經介紹過,快照檔案實際上為一個模糊快照,直到事務日誌重現之後才會成為一個有效的快照檔案。因此在恢復系統時,必須從快照字尾的zxid開始重現事務日誌檔案,甚至更早的zxid開始重現事務。

快照檔案中儲存的模糊快照資訊同樣為二進位制格式,因此,可以通過另一個工具類來檢查快照檔案的內容:

java -cp ZK_LIBS org.apache.zookeeper.server.SnapshotFormatter version-2 /snapshot.100000009
這個命令的輸出資訊如下:
----
/
cZxid = 0x00000000000000
ctime = Wed Dec 31 16:00:00 PST 1969
mZxid = 0x00000000000000
mtime = Wed Dec 31 16:00:00 PST 1969
pZxid = 0x00000100000002
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x00000000000000
dataLength = 0
----
/sasd
cZxid = 0x00000100000002
ctime = Wed Jun 05 07:50:56 PDT 2013
mZxid = 0x00000100000002
mtime = Wed Jun 05 07:50:56 PDT 2013
pZxid = 0x00000100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x00000000000000
dataLength = 3
----
....
只有每個節點的後設資料被轉儲列印出來,這樣,運維人員就可以知道一個znode節點何時發生了變化,以及哪個znode節點佔用了大量記憶體。很遺憾,資料資訊和ACL策略並沒有出現在輸出中,因此,在進行問題診斷時,記住將快照中的資訊與日誌檔案的資訊結合起來分析問題所在。

3、時間戳檔案

ZooKeeper的持久狀態由兩個小檔案構成,它們是兩個時間戳檔案,其檔名為acceptedEpoch和currentEpoch,之前已經討論過時間戳的概念,而這兩個檔案則反映了某個伺服器程式已接受的和正在處理的資訊。雖然這兩個檔案並不包含任何應用資料資訊,但對於資料一致性卻至關重要,所以如果你在備份一個ZooKeeper伺服器的原始資料檔案時,不要忘了這兩個檔案。

4、已儲存的ZooKeeper資料的應用

ZooKeeper資料儲存的一個優點是,不管獨立模式的伺服器還是叢集方式的伺服器,資料的儲存方式都一樣。之前已經介紹過通過事務日誌和快照的合併可以得到準確的資料檢視,可以將事務日誌檔案和快照檔案拷貝到另一臺裝置上進行這些操作(例如在你的便攜電腦中),將這些檔案放到一個獨立模式的伺服器下空白的data目錄下,然後啟動服務,該服務就會真實反映出你所拷貝的那個伺服器上的狀態資訊。這項技術可以從生產環境拷貝伺服器的狀態資訊,用於稍後的複查等用途。

同時也意味著,只需要簡單地將這些資料檔案進行備份,就可以輕易地完成ZooKeeper伺服器的備份,如果採用這種方式進行備份,還需要注意一些問題。首先,ZooKeeper為複製服務,所以系統中存在冗餘資訊,如果進行備份操作,只需要備份其中一臺伺服器的資料資訊。

當ZooKeeper伺服器認可了一個事務,從這時起它就會承諾記錄下該狀態資訊,一定要記住這一點,這一點非常重要。因此如果使用舊的備份檔案恢復一個伺服器,就會導致伺服器違反其承諾。如果剛剛遭遇了所有伺服器的資料丟失的情況,這可能不是什麼大問題,但如果你的叢集在正常工作中,而你將某個伺服器還原為舊的狀態,你的行為可能會導致其他伺服器也丟失了某些資訊。

如果要對全部或大多數伺服器進行資料丟失的恢復操作,最好的辦法是使用最新抓取的狀態資訊(從最新的存活伺服器中獲取的備份檔案),並在啟動伺服器之前將狀態資訊拷貝到其他所有伺服器上。

七、四字母命令

四字母命令提供的這一簡單方法,可以讓對系統進行各種各樣的檢查。四字母命令的主要目標就是提供一個非常簡單的協議,使使用簡單的工具,如telnet或nc,就可以完成系統健康狀況檢查和問題的診斷。為簡單起見,四字母命令的輸出也是可讀形式,使得更容易使用這些命令。

對伺服器新增一個新的命令也很容易,命令列表也就會增長。本節中將會介紹一些常用的命令,對於最新的全部命令列表資訊,請參考ZooKeeper文件。

  • ruok
    提供(有限的)伺服器的狀態資訊。如果伺服器正在執行,就會返回imok響應資訊。事實上“OK”狀態只是一個相對的概念,例如,伺服器執行中,雖無法與叢集中其他伺服器進行通訊,然而該伺服器返回的狀態仍然是“OK”。對於更詳細資訊及可靠的健康狀態檢查,需要使用stat命令。
  • stat
    提提供了伺服器的狀態資訊和當前活動的連線情況,狀態資訊包括一些基本的統計資訊,還包括該伺服器當前是否處於活動狀態,即作為群首或追隨者,該伺服器所知的最後的zxid資訊。某些統計資訊為累計值,可以使用srst命令進行重置。
  • srvr
    提供的資訊與stat一樣,只是忽略了連線情況的資訊。
  • dump
    提供會話資訊,列出當前活動的會話資訊以及這些會話的過期時間。該命令只能在群首伺服器上執行。
  • conf
    列出該伺服器啟動執行所使用的基本配置引數。
  • envi
    列出各種各樣的Java環境引數。
  • mntr
    提供了比stat命令更加詳細的伺服器統計資料。每行輸出的格式為key<tab>value。(群首伺服器還將列出只用於群首的額外引數資訊)。
  • wchs
    列出該伺服器所跟蹤的監視點的簡短摘要資訊。
  • wchc
    列出該伺服器所跟蹤的監視點的詳細資訊,根據會話進行分組。
  • wchp
    列出該伺服器所跟蹤的監視點的詳細資訊,根據被設定監視點的

znode節點路徑進行分組。

  • cons,crst
    cons命令列出該伺服器上每個連線的詳細統計資訊,crst重置這些連線資訊中的計數器為0。

八、通過JMX進行監控

四字母命令可以用於系統的監控,但卻沒有提供系統控制和修改的方法,ZooKeeper通過標準Java管理協議,JMX(Java管理擴充套件),提供了更強大的監控和管理功能。

本節中,將會使用一個簡單的管理控制檯工具jconsole來探索通過JMX管理ZooKeeper功能。

jconsole為Java中自帶的工具,實際上,類似jconsole這樣的JMX工具常常用於監控遠端的ZooKeeper伺服器,但出於說明的目的,將會在ZooKeeper服務所執行的裝置執行該工具。

首先、啟動第二個ZooKeeper伺服器(即ID為2的伺服器),之後,只需要在命令列中簡單的輸入jconsole命令就可以啟動jconsole工具,jconsole啟動後,就會看到類似圖10-7中所示的視窗。

注意到其中帶有“zookeeper”名稱的程式,對於本地程式,jconsole會自動發現可連線的程式。
【圖10-7:jconsole啟動介面】
clipboard.png

現在,只需要在列表中雙擊該程式就可以連線到ZooKeeper程式上,因為沒有啟用SSL,此時會提示關於非安全連線的選項,單擊非安全連線按鈕,之後螢幕中就會出現圖10-8所示的視窗。
【圖10-8:程式管理的第一個視窗】
clipboard.png

從這個介面中看到,可以通過該工具獲取關於ZooKeeper伺服器的各種各樣有趣的統計資訊。JMX支援通過MBean(託管Bean)來將自定義資訊暴露給遠端管理者,雖然名字聽起來比較笨拙,但卻是暴露資訊和操作的一個非常靈活的方式。jconsole會在最右側的資訊標籤中列出程式暴露的所有MBean資訊,如圖10-9所示。
【圖10-9:jconsole中MBean資訊】
clipboard.png

在MBean列表中可以看到ZooKeeper所使用且暴露出來的元件資訊,比較關心ZooKeeperService的資訊,因此雙擊該列表項,將會看到一個分級的列表副本以及這些副本的資訊,如果開啟某些列表中的子項,會看到圖10-10所示的資訊。
【圖10-10:jconsole中關於伺服器2的資訊】
clipboard.png

通過瀏覽replica.2的資訊,注意到這些資訊中還包括其他副本
的資訊,但只是一些通訊資訊,因為伺服器2對其他副本所知資訊並不多,所以伺服器2無法展示更多其他副本資訊,而伺服器2很瞭解自己的資訊,所以它可以暴露更多的資訊。

當啟動伺服器1,伺服器2就可以與伺服器1構成一個法定人數,此時就可以看到伺服器2的更多資訊。啟動伺服器1,之後再次通過jconsole檢查伺服器2資訊。圖10-11展示了通過JMX暴露的一些額外資訊,可以看到伺服器2當前角色為追隨者,還可以看到資料數的資訊。

圖10-11還展示了伺服器1的一些資訊,看到,伺服器1的角色為群首角色,同時還有一些額外資訊,僅在群首伺服器中,FollowerInfo中還會展示追隨者的列表。當點選該按鈕,會看到連線到伺服器1的其他ZooKeeper伺服器的原始列表資訊。
【圖10-11:jconsole中關於伺服器1的資訊】
clipboard.png

到目前為止,看到相比四字母命令,通過JMX所看到的資訊更加優美直觀,但是還沒有看到有什麼新功能,現在看看JMX可以做到而四字母命令無法做到的功能。啟動zkCli指令碼工具,連線到伺服器1,之後執行以下命令:

create -e /me "foo"

通過該命令,會建立一個臨時性的znode節點,圖10-11所示的伺服器1的JMX資訊中,可以看到出現了一個關於連線的新資訊項,連線的屬性中列出了各種各樣的資訊,這些資訊對於除錯執行問題非常有用。這個檢視中,還看到兩個有意思的操作:terminateSession和terminateConnection。

terminateConnection操作會關閉ZooKeeper客戶端到伺服器之間的連線,而會話依然處於活躍狀態,所以此時客戶端還可以重新連線到另一個伺服器,客戶端會收到失去連線的事件,但可以輕易恢復。

與之相反,terminateSession操作會宣告會話已經死亡,客戶端與伺服器之間的連線將會被關閉,且會話也將因過期而中止,客戶端也不能再使用這個會話與其他伺服器建立連線。因此,使用terminateSession操作時需要小心,因為該操作會在會話超時之前就導致會話過期,所以在該程式自己發現過期前,其他程式可能已經發現了該程式的會話死亡的情況。

遠端連線

JMX代理器執行於ZooKeeper伺服器的JVM之中,如果連線遠端的ZooKeeper伺服器,需要配置好JMX代理器。對與遠端連線的JMX協議有若干引數需要配置,本節中,展示了一種JMX的配置方式,來看看JMX提供了什麼樣功能。如果在生產環境使用JMX,可能需要使用另一種JMX配置——具體參考如何配置更高階的安全功能。

對JMX的配置可以通過系統屬性的方式進行配置,在用於啟動ZooKeeper伺服器的zkServer.sh指令碼中,使用SERVER_JVMFLAGS環境變數來配置這些系統屬性。

例如,以下面的配置啟動服務,就可以遠端連線伺服器3的55555埠。

SERVER_JVMFLAGS="-Dcom.sun.management.jmxremote.password.file=passwd \
-Dcom.sun.management.jmxremote.port=55555 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.access.file=access"
 _path_to_zookeeper_/bin/zkServer.sh start   _path_to_server3.cfg_

系統屬性引數中用到了密碼檔案和訪問控制檔案,這些檔案的格式非常簡單。首先,建立passwd檔案,方式如下:

# user password
admin <password>

注意,密碼檔案中的密碼資訊為明文儲存,因此只能給密碼檔案的所有者分配讀寫許可權,如果不這樣做,Java就無法啟動服務。同時,關閉了SSL功能,這意味著密碼資訊在網路上會以明文的方式進行傳輸,如果需要更強的安全級別,JMX也提供了更強有力的選項,但這些已經超出本書所涉及的範圍。

對於訪問控制檔案,將會給admin使用者賦予readwrite許可權,建立該檔案的方式如下:

admin readwrite

九、工具

在ZooKeeper發行包的contrib目錄中,可以找到一些軟體,這些軟體可以幫你整合ZooKeeper到其他的檢測系統中去。列出了一部分最受歡迎的軟體或工具:

  • 通過C繫結實現的Perl和Python語言的繫結庫。
  • ZooKeeper日誌視覺化的軟體。
  • 一個基於網頁的叢集節點瀏覽和ZooKeeper資料修改功能的軟體。
  • ZooKeeper中自帶的zktreeutil和guano均可以從GitHub下載。這些軟體可以對ZooKeeper的資料進行匯入和匯出操作。
  • zktop,也可以從GitHub下載,該軟體監控ZooKeeper的負載,並提供Unix的top命令類似的輸出。
  • ZooKeeper冒煙測試,可以從GitHub上下載。該軟體對ZooKeeper叢集提供了一個簡單的冒煙測試客戶端,這個工具對於開發人員熟悉ZooKeeper非常不錯。

相關文章