ZooKeeper動態配置(十四)


概述

在3.5.0發行之前,ZK的全體成員和所有其它的配置參數是靜態加載的在啟動的時候並且在運行的時候不可變。操作員訴諸於"滾動重啟" - 一個手動密集和改變配置文件容易出錯的方法,導致在生產環境數據丟失和不一致。從3.5.0開始,"滾動重啟"不再需要!ZK開始支持自動地配置改變:ZK服務端的設置,它們的角色(參與者/觀察者),所有端口,甚至法定人數系統可以被動態地改變,而不用服務中斷和維修數據一致性。重新配置被立刻執行。就像ZK中的其它操作。很多改變可以通過使用一個重新配置命令完成。動態重新配置功能不限制操作的並發,不要求客戶端操作停止在重新配置時,擁有一個非常簡單的管理者接口,並且不會對其它客戶端操作增加復雜性。

新的客戶端特性允許客戶端發現配置改變,並且更新存儲在它們的ZK句柄中的連接字符串(服務端列表和它拉的客戶端端口)。一個概率算法被使用來平衡新配置的服務端上的客戶端。同時保持客戶端遷移的程度與集群成員的變化成正比。

這個文檔提供了重新配置的管理員手冊。對於重新配置的算法,性能測量,還有其它更多的詳細描述請參考我們的論文:Shraer, A., Reed, B., Malkhi, D., Junqueira, F. Dynamic Reconfiguration of Primary/Backup Clusters. In USENIX Annual Technical Conference (ATC) (2012), 425-437

連接:paper (pdf), slides (pdf), video, hadoop summit slides

配置格式的改變

指定客戶端商品

服務端的客戶端端口是服務端接受客戶端連接請求的端口。從3.5.0開始clientPort和clientPortAddress配置參數不應該被使用。相反地,這個信息現在是服務端關鍵詞規范的一部分。它變成下面這樣:

server.<positive id> = <address1>:<port1>:<port2>[:role];[<client port address>:]<client port>

 客戶端端口的規范是在分號的右邊。客戶端端口地址是可選的,如果沒有指定它默認是"0.0.0.0"。想往常一樣,角色也是可選的,它可以是participant或者observer(默認是participant)。

合法的服務端聲明:

  • server.5 = 125.23.63.23:1234:1235;1236

  • server.5 = 125.23.63.23:1234:1235:participant;1236

  • server.5 = 125.23.63.23:1234:1235:observer;1236

  • server.5 = 125.23.63.23:1234:1235;125.23.63.24:1236

  • server.5 = 125.23.63.23:1234:1235:participant;125.23.63.23:1236

standaloneEnabled 標識

在3.5.0之前,我們可以以單機模式運行ZK或以分布式模式。他們是不同的實現,並且在運行時切換是不可能的。默認(為了向后兼容)standaloneEnabled被設置成true。使用這個默認值的結果是如果啟動一個單獨的服務端群集將不會起來,並且如果啟動多於一個服務端它將不允許縮小到小於兩個參考者。

設置這個標識為false指示系統以分布式運行,即使集群里只有一個參與者。為了實現這樣配置文件應該包含:

standaloneEnabled=false

有了這個設置可以啟動只包含一個參與者的集群,並且可以動態地增加更多服務端。相似地,它也可以縮小一個群集到只有一個參考者,通過移除服務端。

因為運行在分布式模式允許更多靈活性,我們建議設置這個標識為false。我們期待遺留的單機模式在未來將被標識為過時的。

動態配置文件

從3.5.0開始我們開始區分動態配置參數,它可以在運行時被改變,和靜態配置參數,當服務端啟動的時候從配置文件中讀取並且不能改變在運行的時候。現在下面的配置關鍵詞被認識是動態配置的一部分:server, groupweight

動態配置參數被存儲在服務端一個單獨的文件中(我們稱它為動態配置文件)。這個文件被連接在靜態文件中使用dynamicConfigFile關鍵詞。

例子

zoo_replicated1.cfg

tickTime=2000
dataDir=/zookeeper/data/zookeeper1
initLimit=5
syncLimit=2
dynamicConfigFile=/zookeeper/conf/zoo_replicated1.cfg.dynamic

zoo_replicated1.cfg.dynamic

server.1=125.23.63.23:2780:2783:participant;2791
server.2=125.23.63.24:2781:2784:participant;2792
server.3=125.23.63.25:2782:2785:participant;2793

當我們集成配置改變,靜態配置參數保持不變。動態的參數被ZK推出並且在所有的服務端覆蓋動態配置文件。因此,在不同服務端的動態配置文件通常是一樣的(他們可以只有瞬間不同當一個重新配置正在處理中,或者如果一個新的配置還沒有傳播到一些服務端)。一旦被創建,動態的配置文件不應該被手動地修改。改變只能通過下面標出的新的重新配置命令實現。注意改變一個離線的集群的配置可能導致不一致相對於配置文件被存儲在ZK的日志(特殊的配置znode,從日志中填充),並且因此非常地不推薦。

例子2

用戶可能更傾向於一開始就指定一個配置文件。因此下面的也是合法的:

zoo_replicated1.cfg

tickTime=2000
dataDir=/zookeeper/data/zookeeper1
initLimit=5
syncLimit=2
clientPort=2791  // note that this line is now redundant and therefore not recommended
server.1=125.23.63.23:2780:2783:participant;2791
server.2=125.23.63.24:2781:2784:participant;2792
server.3=125.23.63.25:2782:2785:participant;2793

在每一個服務端的配置文件將被自動地分為自動和靜態文件,如果他們不是以這種格式。所以上面的配置文件將被自動地轉換為在例子1中的兩個文件。注意clientPort和clientPortAddress行(如果指定了)在這個過程中將被自動地移除,如果他們是冗余的(就像上面例子中的一樣)。原來 的靜態文件被備份(以一個.bak文件)。

向后兼容

我們仍然支持老的配置格式。例如,下面的配置文件是可接受的(但是不建議)

zoo_replicated1.cfg

tickTime=2000
dataDir=/zookeeper/data/zookeeper1
initLimit=5
syncLimit=2
clientPort=2791
server.1=125.23.63.23:2780:2783:participant
server.2=125.23.63.24:2781:2784:participant
server.3=125.23.63.25:2782:2785:participant

在啟動的時候,一個動態的配置文件被創建並且包含先前解釋的配置的動態部分。然而在這個例子中,"clientPort=2791"行將保留在server 1的靜態配置文件中,因為它不是冗余的 --它沒有被指定作為 "server.1=..."的一部分,使用在Changes to Configuration Format部分講解的格式。如果一個重新配置被調用來設置server 1的客戶端部分,我從靜態配置文件中刪除"clientPort=2791"。(現在動態文件包含這個信息作為server1規范的一部分)。

升級到3.5.0

升級一個運行中的集群到3.5.0應該被完成只有在升級你的集群到3.4.6之后。注意這只對滾動升級的時候才是必須的(如果你可以完全地關閉系統,你不需要經過3.4.6)。如果你試圖滾動升級而不經過3.4.6(例如從3.4.5),你可能得到下面的錯誤:

2013-01-30 11:32:10,663 [myid:2] - INFO [localhost/127.0.0.1:2784:QuorumCnxManager$Listener@498] - Received connection request /127.0.0.1:60876
2013-01-30 11:32:10,663 [myid:2] - WARN [localhost/127.0.0.1:2784:QuorumCnxManager@349] - Invalid server id: -65536

在滾動升級期間,每一個服務端輪流被取掉並且以新的3.5.0包重啟。在啟動3.5.0的包之前,我們強烈建議更新配置文件以便所有的服務端聲明"server.x=..."包含客戶端端口(參考Specifying the client port的部分)。正如前面講解的那樣,你可以把配置放在一個的文件中,clientPort/clientPortAddress聲明也一樣。(盡管如果你指定客戶端商品以新的格式,這些聲明現在就是冗余的)。

ZK群集的動態重新配置

ZK的Java和C的API暴露了getConfig和reconfig命令來幫助重新配置。兩個命令都有一個同步(阻塞)變體和一個異步變體。我們這里演示這些命令使用有Java 命令行界面,但是注意你可以相似地使用C命令行界面或者直接從程序中地調用這個命令就像其它ZK命令。

檢索當前動態的配置

動態的配置被存儲在一個特別的znodeZooDefs.CONFIG_NODE=/zookeeper/config。新的config命令行界面命令讀這個znode(當前它僅僅是一個包裝 get /zookeeper/config)。就像普通的讀取,為了檢索到最新的提交的值,你應該先做一個sync。

[zk: 127.0.0.1:2791(CONNECTED) 3] config
server.1=localhost:2780:2783:participant;localhost:2791
server.2=localhost:2781:2784:participant;localhost:2792
server.3=localhost:2782:2785:participant;localhost:2793
version=400000003

注意輸出的最后一行。這是配置版本號。這個版本號等於創建這個配置的重新配置命令的zxid。第一個被建立的配置版本號等於被第一個成功建立的領導者發出的NEWLEADER消息的zxid。當一個配置被寫入到一個動態配置文件,版本號自動地變為文件名的一部分並且靜態配置文件被更新帶着指向的新的動態配置文件的路徑。被保留的早期版本的配置文件是為了備份目的。

在啟動期間版本號(如果存在)被從文件名中提取出來。版本號應該永遠不被用戶手動地修改或系統管理員。它被系統用來確定那一個配置是最新的。手動地修改可能導致數據丟失和不一致性。

就像一個get命令,config命令行命令接受-w標識來在這個znode上設置一個監視器,-s標識用來顯示znode的統計。它另外接受一個新的標識 -c ,它只輸出版本號和當前配置對應的連接字符串。例如,對於上面的配置,我們可以get:

[zk: 127.0.0.1:2791(CONNECTED) 17] config -c
400000003 localhost:2791,localhost:2793,localhost:2792

注意當直接使用API,這個命令被稱為getConfig。

像任何讀命令它返回你的客戶端連接的追隨者知道的配置,可能有一點過期。可以使用sync命令來多一點保證。例如使用Java API:

zk.sync(ZooDefs.CONFIG_NODE, void_callback, context);
zk.getConfig(watcher, callback, context);

注意:在3.5.0中它不真的關心傳給sync()的路徑,因為所有服務端的狀態都被帶到和領導者相同的日期。(所以你可以使用一個不同的路徑而不是ZooDefs.CONFIG_NODE)然而這可能在未來被改變。

修改當前動態配置

通過reconfig命令來完成配置的修改。有兩個重修改的模式:增量和非增量(bulk)。非增加的簡單地指定系統新的動態配置。增量的指定當前配置的改變。reconfig命令返回新的配置。

幾個例子在:ReconfigTest.java, ReconfigRecoveryTest.java and TestReconfigServer.cc。

通常

刪除服務端:

任何服務端可以被刪除,包括領導者(盡管刪除領導者將導致一小會兒的不可胳膊,參考paper中的圖6和8)。服務端將不會被自動地關閉。相反,它變成一個"不投票的追隨者"。這有點類似於一個觀察者因為它的投票不被計算在提交操作的投票的法定人數當中。然而,和非投票的追隨者不一樣的是,觀察者不會真正地看到任何操作提議和不會回應它們。因此一個非投票的追隨者具有更大的負面影響對於系統的吞吐量相對於觀察者。非投票的追隨者模型應該只被用作臨時模型。在關閉服務端之前或者增加一個追隨者或觀察者到集群中。我們不自動地關閉服務端為了兩個主要原因。第一個是我們不想所有連接到這個服務端的連接立刻失連,導致大量的連接請求到其它服務端。相反地,如果每一個服務端決定什么時候自主地遷移是更好的。第二個原因是刪除一個服務端可能很少是必須的為了把它從“觀察者”變為“參考者”(這在Additional comments部分講解過)。

注意新的配置應該有最小數量的參考者為了被認為是合法的。如果提出的改變將使集群小於2個參考者和單機模式被啟用(standaloneEnabled=true,參考The standaloneEnabled flag部分),這個重新配置操作將不會被執行。如果單機模式被禁用(standaloneEnabled=false)那么它是合法的剩下1個或更多個參考者。

增加服務端:

在重新配置被調用之前,管理者必須確保從新配置中的參與者的法定人數已經連接上並且和當前的領導者同步過。為了達到這個目的,我們需要連接一個新加入的服務端到領導者在它正式地成為集群的一部分之前。這通過啟動正在加入的服務端使用一個初始的服務端列表來完成,它從技術上來說不是一個合法的系統配置,但是(a)包含加入者,和(b)提供出充足的信息給加入者為了它找到和連接上當前的領導者。我們列出更安全地做這個事的一些不用的選項。

  1. 加入者的初始的配置包含了最后提交的配置服務端和1個或更多個加入者,這里加入者被作為觀察者列出。例如如果服務端D和E被同時加入到(A,B,C)並且C正在被刪除,D的初始配置將是(A,B,C,D)或者(A,B,C,D,E),這里D和E被作為觀察者列出。相似地,E的配置將是(A,B,C,E)或者(A,B,C,D,E),這里D和E被作為觀察者列出。注意作為觀察者列出的加入者將不會真實地使他們成為觀察者 - 它只是防止他們偶然地和其它加入者形成一個法定人數。相反地,他們將連接服務端用當前的配置並且采用最后提交的配置(A,B,C),這里的加入者不存在。加入者的配置文件被備份起來並且被自動地替換錄這發生。在連接到當前的領導者之后,加入者變為非投票的追隨者直接系統被重新配置並且他們被加入到集群(作為參考者或才觀察者,視情況而定)
  2. 每一個加入者的初始配置包含了最后提交的配置服務端和加入者自己,作為參與者被列出。例如,增加一個新的服務端D到一個(A,B,C),管理者可以啟動D使用一個包含(A,B,C,D)的初始配置文件。如果D和E同時加入到(A,B,C),D的初始配置將會是(A,B,C,D)和E的初始配置將會是(A,B,C,E)。相似地,如果同時D被加入和C被刪除,D的初始配置將會是(A, B, C, D)。永遠不要列出超過1個作為參與者的加入者在初始配置中(參考下面的警告)
  3. 作為觀察者或參考者列出加入者,所有的當前服務端不列出也是可以的,只要當前的領導者在列表中。例如,當增加D我們可以啟動D使用一個只包含(A,D)的配置文件如果A是當前的領導者。然而這是更脆弱的因為如果A在D正式加入群集之前失效,D不知道任何其它的服務端並且因此管理員將不得不干預並且重啟D用另一個服務端列表。

警告

永遠不要在相同的初始配置中指定多於一個作為參考者加入的服務端。當前,正在加入的服務端不知道他們正在加入一個存在的集群;如果多個加入者被作為參考者列出,他們可能形成一個獨立的法定人數創建一個腦裂的情況例如獨立於你的主集群中處理操作。作為觀察者列表多個加入者是OK的。

最后,注意一旦連接到一個領導者,加入者接受最后提前的配置,在這里它是不在的(加入者的初始配置被備份在被重寫之前)。如果加入者在這個狀態重啟,它將不能啟動因為它是不存在它的配置文件中的。為了啟動它,你將不得不再次指定一個配置文件。

 修改服務端參數:你可以修改服務端的任何端口,或者它的角色(參與者/觀察者)通過加入它到集群中使用不同的參數。這可以以增量和非增量的重新配置模式一實現。不必要刪除服務端然后再加回來。只需要指定新的參數就好像服務端還沒有在系統中。服務端將檢測到配置改變並且執行必要的調整。參考Incremental mode部分的例子和Additional comments部分這個規則的一個例外。

也可以通過集群改變法定人數系統(例如,實時地改變多數法定人數系統為分層法定人數系統)。然而這只能使用非增加地重新配置模式。通常增量的重新配置只和多數法定人數系統一起工作。非增加重新配置可以和分層和多數法定人數系統工作。

性能影響:當刪除一個追隨者的時候沒有特別地性能影響,因為它不能自動地關閉(刪除的影響是這個服務端的投票不再被計算在內)。當增加一個服務端,沒有領導者改變和沒有可觀察性能中斷。更詳細的內容和曲線圖請參考paper中的Figures6,7,8。

更嚴重的中斷將會發生當領導者改變以下面的案例發生:

  1. 領導者從集群中被刪除。
  2. 領導者的角色從參與者被改變為觀察者
  3. 被領導者用來發送事務的端口被修改。

在這種情況下我執行一個領導者傳遞,老的領導者提名一個新的領導者。導致的不可用通常比當一個領導者崩潰進的時間少因為檢測領導者失效不是必須的,並且選舉一個新的領導者通常可以被避免在傳遞的過程中(參考 paper中的Figures6 和8)

當服務端的客戶端端口被修改,它不丟棄正在存在的客戶端連接。到這個服務端的新連接將不得不使用新的客戶端商品。

過程保證:取決於reconfig操作的調用,老配置的法定人數要求是可用的並且對ZK的連接可以取得進展。一旦reconfig被調用,老的和新的配置的法定人數必須是可用的。最后的事務發生一旦(a)新的配置被啟用。並且(b)在新的配置被啟用之前所有操作被提交。一旦a和b發生,只有新配置的法定人數是需要的。然而請注意a和b對客戶端來說都是不可見的。特別地,當一個重新配置操作提交,它只意為一個激活消息被領導者發送出去。它不絕對意為它新配置的法定人數獲取這個消息(為了激活它是需要的)或者b已經發生。如果你想確保a和b都已經發生(例如,為了知道關閉被刪除的老的服務端是安全的),你可以簡單地調用一個更新(set-data,或者其它法定人數操作,但是不能是sync)並且等待它提交。另一種方法來實現這是引進另一個輪的重新配置協議(為了簡單和兼容Zab,我們決定避免這種方式)。

增量模式

增量模式允許增加和刪除當前配置的服務端。多個改變被允許。例如:

> reconfig -remove 3 -add server.5=125.23.63.23:1234:1235;1236

增加和刪除選項都有一個逗號分割的參數列表(沒有空格):

> reconfig -remove 3,4 -add server.5=localhost:2111:2112;2113,6=localhost:2114:2115:observer;2116

服務端聲明的格式和在Specifying the client port部分描述的一模一樣,並且包含客戶端端口。注意這里代替"server.5",你可以僅僅用"5="。在上面的例子中,如果server 5已經存在系統中,但是有不同的端口或者不是一個觀察者,它被更新並且一旦配置提交變為一個觀察者並且使用新端口啟動。這是一個簡單的方式把參與者變為觀察者和返過來。或者改變他們端口中的任何一個,而不用重啟服務端。

ZK支持兩種法定人數系統 - 簡單的多數系統(領導者提交操作在收到多數投票的確認信息后)和一個更復雜的分層系統,不同的服務端的投票有不同的權重並且服務端被分為投票組。當前,增量重新配置只被允許如果最后提議的配置被使用多數法定人數系統的領導者知道(否則拋出BadArgumentsException)。

增量模式 - 使用Java API的例子:

List<String> leavingServers = new ArrayList<String>();
leavingServers.add("1");
leavingServers.add("2");
byte[] config = zk.reconfig(null, leavingServers, null, -1, new Stat());
List<String> leavingServers = new ArrayList<String>();
List<String> joiningServers = new ArrayList<String>();
leavingServers.add("1");
joiningServers.add("server.4=localhost:1234:1235;1236");
byte[] config = zk.reconfig(joiningServers, leavingServers, null, -1, new Stat());

String configStr = new String(config);
System.out.println(configStr);

也有一個異步的API,這個API接受逗號分隔的字符串而不是List<String>。參考src/java/main/org/apache/zookeeper/ZooKeeper.java。

非增量模式

第種重新配置的模式是非增量,客戶端提供一個完整的新的動態系統配置規范。新的配置可以從一個文件中給出或從一個文件中讀取:

> reconfig -file newconfig.cfg ///newconfig.cfg 是動態配置文件 參考Dynamic configuration file

> reconfig -members server.1=125.23.63.23:2780:2783:participant;2791,server.2=125.23.63.24:2781:2784:participant;2792,server.3=125.23.63.25:2782:2785:participant;2793

新配置可能使用一個不同的法定人數系統。例如,你可以指定一個分層法定人數系統即使當前的集群使用多數法定人數系統。

非增量模式 - 使用Java API 的例子:

ArrayList<String> newMembers = new ArrayList<String>();
newMembers.add("server.1=1111:1234:1235;1236");
newMembers.add("server.2=1112:1237:1238;1239");
newMembers.add("server.3=1114:1240:1241:observer;1242");

byte[] config = zk.reconfig(null, null, newMembers, -1, new Stat());

String configStr = new String(config);
System.out.println(configStr);

同時也有一個異步API,這個API接受逗號分隔的包含List<String>的新成員的字符串。參考src/java/main/org/apache/zookeeper/ZooKeeper.java。

條件reconfig

有時候(特別是非增量模式)一個新提出的配置取決於客戶端depends on what the client "believes" to be the current configuration,並且應該只應用這個配置。特別地,reconfig成功只有領導者的最后的配置文件有指定的版本號。

> reconfig -file <filename> -v <version>

在前面列出的Java例子,除了-1,你可以指定一個配置版本來給這個重新配置加條件。

錯誤條件除了普通的ZK錯誤條件,重新配置可以因為下面的原因而失敗:

  1. 另一個reconfig現在正在進行(ReconfigInProgress)
  2. 提出的改變將使集群小於2個參與者,在單機模式啟用的情況下,或者如果單機模式被禁用那么它是合法的保留1或多個參與者(BadArgumentsException)
  3. 新配置沒有法定人數被連接和更新和領導者當重新配置開始處理(NewConfigNoQuorum)
  4. -v x被指定,但是最新的配置版本號y不是x(BadVersionException)
  5. 一個增量的重新配置被請求但是領導者最新的配置使用一個不多數系統不同的法定人數系統(BadArgumentsException)
  6. 語法錯誤(BadArgumentsException)
  7. I/O異常當從一個文件中讀取配置(BadArgumentsException)

他們的大部分被說明在測試用例ReconfigFailureCases.java中。

附言

Liveness:為了更好的理解增量和非增量之間的不同,假設客戶端C1增加服務端D到系統中,同時一個不同的客戶端C2增加服務端E。對於非增量模式,每一個客戶端將首先調用config來找出當前的配置,然后在本地創建一個新的服務端列表通過增加他們自己建議的服務端。新配置然后可以被提交使用有非增量的reconfig命令。在兩個重新配置都完成之后,只有E或D中的一個將被加入(不是兩個),取決於那一個客戶端的請第二個到達領導者,覆蓋了先前的配置。其它的客戶端可以重復這個過程只到它的改變生效。這個方法保證全系統的進展(也就是說,客戶端中的一樣),但是不能確保每一個客戶端成功。為了對C2更多控制可以請求只執行重新配置在當前配置沒還有改變的情況下。正如在Conditional reconfig中講解的那樣。用這種方式它可以避免盲目地覆蓋C1的配置文件,如果C1的配置先到達領導者。

對於增加重新配置,兩個改變將生效因為他們簡單地被應用被領導者一個接一個應用到當前的配置,不管它是什么(假如第二個reconfig請求到達領導者在第一個reconfig請求發送一個提交消息 -- 現在領導者將將拒絕提議一個重新配置如果另一個已經正在懸而未決)。因為這兩個客戶都保證取得進展,這個方法保證更強的liveness。在實踐中,多個並發的重新配置可能是罕見的。非增量的重新配置是當前唯一的方式來動態地改變多數系統。增量的配置是當前唯一被允許的對於多數法定人數系統。

改變一個觀察者為一個追隨者:顯然地,改變一個參與投票的服務端為一個觀察者可能失敗如果錯誤(2)發生。也就是說,如果小於參與者的最小被允許的數量。然而,轉變一個觀察者為一個參與者有時可能會失敗因為一個更微妙的原因:假如,例如當前的配置是(A,B,C,D),這里A是領導者,B和C是追隨者並且D是一個一觀察者。另外,假如B已經崩潰。如果一個重新配置被提交,這個配置使D變為一個追隨者,它將會失敗因為錯誤(3)因為在這個配置中,新配置中的多數投票者(任何3個投票者),必須被連接和更新和領導者。一個觀察者無法確認在重新配置過程中發送的歷史前綴,因此,它不指望這3個所需的服務器並且重新配置將會被取消。如果發生這種情況,一個客戶端可以達到相同的任務通過兩個reconfig命令:首先調用reconfig命令來從配置中刪除D然后調用第二個命令來把它增加回來作為參考者(追隨者)。在中間狀態D是一個不投票的追隨者並且可以可以響應在第二個reconfig命令執行其中的狀態轉換。

平衡客戶端連接

當一個ZK集群被啟動,如果每一個客戶端使用相同的連接字符串(服務端列表),客戶端將隨機地選擇一個服務端來連接,使每個服務器的客戶端連接的預期數量相同。我們實現一個方法來保證這個特性當通過重新配置服務端列表改變。參考paper中的4和5.1部分。

為了使這個方法起作用,所有客戶端必須訂閱配置改變(通過在/zookeeper/config上設置一個監視器,直接地或通過getConfigAPI命令)。當監視器被觸發,客戶端應該讀取新的配置通過調用sync和getConfig,並且如果配置確實是新的,調用updateServerList API命令。為了避免大量的客戶端同時遷移,最好每一個客戶端睡覺一個隨機的時間在調用updateServerList之前。

一些例子可以被找到在:StaticHostProviderTest.java TestReconfig.cc

例子(這不是食譜,而是為了講解總體思路的一個簡化的例子):

public void process(WatchedEvent event) {
    synchronized (this) {
        if (event.getType() == EventType.None) {
            connected = (event.getState() == KeeperState.SyncConnected);
            notifyAll();
        } else if (event.getPath()!=null &&  event.getPath().equals(ZooDefs.CONFIG_NODE)) {
            // in prod code never block the event thread!
            zk.sync(ZooDefs.CONFIG_NODE, this, null);
            zk.getConfig(this, this, null);
        }
    }
}
public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
    if (path!=null &&  path.equals(ZooDefs.CONFIG_NODE)) {
        String config[] = ConfigUtils.getClientConfigStr(new String(data)).split(" ");   // similar to config -c
        long version = Long.parseLong(config[0], 16);
        if (this.configVersion == null){
             this.configVersion = version;
        } else if (version > this.configVersion) {
            hostList = config[1];
            try {
                // the following command is not blocking but may cause the client to close the socket and
                // migrate to a different server. In practice its better to wait a short period of time, chosen
                // randomly, so that different clients migrate at different times
                zk.updateServerList(hostList);
            } catch (IOException e) {
                System.err.println("Error updating server list");
                e.printStackTrace();
            }
            this.configVersion = version;
} } }

 

 

  插播個廣告 


老丈人家的粉皮兒,農產品,沒有亂七八糟的添加劑,歡迎惠顧
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM