Zookeeper C API 指南
以前自己的博客中轉載、翻譯或寫過(不過自己才疏學淺,寫的不好)一些 Zookeeper 方面的文章,但是都沒有涉及到 Zookeeper C API 的內容,今天的這篇博客是我農歷新年的第一篇技術博客,我想詳細講講 Zookeeper C API 的使用規則和示例,算是把以前的舊帳還上吧 :-)
Zookeeper 官方頁面上提供了一些編程指南和 API 文檔,不過大部分都是 Java 示例,涉及 C API 的部分很少,只有在 ZooKeeper Programmer's Guide 中 ACL Permissions 一節講了 Zookeeper C API 中設置 ACL 應該注意的事項,正是由于缺少Zookeeper C API 相關的資料,大部分 Zookeeper C/C++ 開發者只能通過閱讀 Zookeeper C API 的源碼來了解 C API 的使用方法,本文希望在此方面給大家提供一些便利,減少 Zookeeper 新手使用 C API 的困難和恐懼,當然我自己也是一枚新手啦 :-)。
廢話不多說了,先從最基本的開始吧!
Zookeeper 偽分布式安裝
首先是 Zookeeper 的安裝,如果你沒有足夠的機器,建議在單機上通過偽分布式安裝方法來模擬 Zookeeper 集群,我在這里提供給大家一個簡單的安裝包來降低偽分布式 Zookeeper 的安裝難度,該安裝包可以模擬一個 5 Zookeeper 實例的集群。下載zookeeper-3.4.0-pseudoclusters.tar.bz2,解壓至 /tmp/ 目錄(注意,安裝包里面 dataDir 和 dataLogDir 配置已經預設為 /tmp/ 目錄,如果你只是自己在 Zookeeper 上做一些小實驗,建議不要更改該配置,這樣在你做完了實驗以后刪除 /tmp/zookeeper/ 目錄即可),操作方法如下:
forhappy@haiping-ict:/tmp$ pwd /tmp forhappy@haiping-ict:/tmp$ tar xvf zookeeper-3.4.0-pseudoclusters.tar.bz2 forhappy@haiping-ict:/tmp$ cd zookeeper/ forhappy@haiping-ict:/tmp/zookeeper$ ls server001 server003 server005 stopZKCluster.sh server002 server004 startZKCluster.sh然后在 /tmp/zookeeper 目錄下執行 startZKCluster.sh 啟動 Zookeeper 服務,或者執行 stopZKCluster.sh 停掉 Zookeeper 服務。
啟動 Zookeeper 服務:
forhappy@haiping-ict:/tmp/zookeeper$ ./startZKCluster.sh . starting zookeeper instance server001... JMX enabled by default Using config: /tmp/zookeeper/server001/zookeeper-3.4.0/bin/../conf/zoo.cfg Starting zookeeper ... STARTED starting zookeeper instance server002... JMX enabled by default Using config: /tmp/zookeeper/server002/zookeeper-3.4.0/bin/../conf/zoo.cfg Starting zookeeper ... STARTED starting zookeeper instance server003... JMX enabled by default Using config: /tmp/zookeeper/server003/zookeeper-3.4.0/bin/../conf/zoo.cfg Starting zookeeper ... STARTED starting zookeeper instance server004... JMX enabled by default Using config: /tmp/zookeeper/server004/zookeeper-3.4.0/bin/../conf/zoo.cfg Starting zookeeper ... STARTED starting zookeeper instance server005... JMX enabled by default Using config: /tmp/zookeeper/server005/zookeeper-3.4.0/bin/../conf/zoo.cfg Starting zookeeper ... STARTED停掉 Zookeeper 服務:
forhappy@haiping-ict:/tmp/zookeeper$ ./stopZKCluster.sh . stopping zookeeper instance server001... JMX enabled by default Using config: /tmp/zookeeper/server001/zookeeper-3.4.0/bin/../conf/zoo.cfg Stopping zookeeper ... STOPPED stopping zookeeper instance server002... JMX enabled by default Using config: /tmp/zookeeper/server002/zookeeper-3.4.0/bin/../conf/zoo.cfg Stopping zookeeper ... STOPPED stopping zookeeper instance server003... JMX enabled by default Using config: /tmp/zookeeper/server003/zookeeper-3.4.0/bin/../conf/zoo.cfg Stopping zookeeper ... STOPPED stopping zookeeper instance server004... JMX enabled by default Using config: /tmp/zookeeper/server004/zookeeper-3.4.0/bin/../conf/zoo.cfg Stopping zookeeper ... STOPPED stopping zookeeper instance server005... JMX enabled by default Using config: /tmp/zookeeper/server005/zookeeper-3.4.0/bin/../conf/zoo.cfg Stopping zookeeper ... STOPPEDZookeeper C API 安裝
Zookeeper C client 的實現在 src/c 目錄下,進入到該目錄安裝 Zookeeper C client,步驟如下:
$ ./configure $ make $ sudo make install至此,基本的準備工作已經完成,你可以通過 Zookeeper 本身提供的 Shell 來操作 Zookeeper,操作方法如下,進入 /tmp/zookeeper/server001/zookeeper-3.4.0 目錄,執行bin/zkCli.sh -server 127.0.0.1:2181 進入 Zookeeper shell:
forhappy@haiping-ict:/tmp/zookeeper/server001/zookeeper-3.4.0$ bin/zkCli.sh -server 127.0.0.1:2181 [zk: 127.0.0.1:2181(CONNECTED) 2] help ZooKeeper -server host:port cmd argsconnect host:portget path [watch]ls path [watch]set path data [version]rmr pathdelquota [-n|-b] pathquit printwatches on|offcreate [-s] [-e] path data aclstat path [watch]close ls2 path [watch]history listquota pathsetAcl path aclgetAcl pathsync pathredo cmdnoaddauth scheme authdelete path [version]setquota -n|-b val path在 shell 中你可以完成基本的操作,如創建、獲取、刪除、設置某一節點,設置節點 ACL等,可以 zookeeper shell 中通過 help 獲取相關命令的用法。
如果你按照上面的步驟完成了 Zookeeper 偽分布式的安裝,并且想繼續了解 Zookeeper C API 的使用方法,請繼續閱讀《Zookeeper C API 指南二(監視(Wathes), 基本常量和結構體介紹)》
接上一篇《Zookeeper C API 指南一(準備工作)》,本問將重點介紹 Zookeeper 監視(Watches),以及 Zookeeper C API 中基本的常量與結構體。
Zookeeper 監視(Watches) 簡介
Zookeeper C API 的聲明和描述在 include/zookeeper.h 中可以找到,另外大部分的 Zookeeper C API 常量、結構體聲明也在 zookeeper.h 中,如果如果你在使用 C API 是遇到不明白的地方,最好看看 zookeeper.h,或者自己使用 doxygen 生成 Zookeeper C API 的幫助文檔。
Zookeeper 中最有特色且最不容易理解的是監視(Watches)。Zookeeper 所有的讀操作——getData(),getChildren(), 和 exists() 都 可以設置監視(watch),監視事件可以理解為一次性的觸發器, 官方定義如下: a watch event is one-time trigger, sent to the client that set the watch, which occurs when the data for which the watch was set changes。對此需要作出如下理解:
-
(一次性觸發)One-time trigger
當設置監視的數據發生改變時,該監視事件會被發送到客戶端,例如,如果客戶端調用了 getData("/znode1", true) 并且稍后 /znode1 節點上的數據發生了改變或者被刪除了,客戶端將會獲取到 /znode1 發生變化的監視事件,而如果 /znode1 再一次發生了變化,除非客戶端再次對 /znode1 設置監視,否則客戶端不會收到事件通知。
-
(發送至客戶端)Sent to the client
Zookeeper 客戶端和服務端是通過 socket 進行通信的,由于網絡存在故障,所以監視事件很有可能不會成功地到達客戶端,監視事件是異步發送至監視者的,Zookeeper 本身提供了保序性(ordering guarantee):即客戶端只有首先看到了監視事件后,才會感知到它所設置監視的 znode 發生了變化(a client will never see a change for which it has set a watch until it first sees the watch event). 網絡延遲或者其他因素可能導致不同的客戶端在不同的時刻感知某一監視事件,但是不同的客戶端所看到的一切具有一致的順序。
-
(被設置 watch 的數據)The data for which the watch was set
這意味著 znode 節點本身具有不同的改變方式。你也可以想象 Zookeeper 維護了兩條監視鏈表:數據監視和子節點監視(data watches and child watches) getData() and exists() 設置數據監視,getChildren() 設置子節點監視。 或者,你也可以想象 Zookeeper 設置的不同監視返回不同的數據,getData() 和 exists() 返回 znode 節點的相關信息,而 getChildren() 返回子節點列表。因此, setData() 會觸發設置在某一節點上所設置的數據監視(假定數據設置成功),而一次成功的 create() 操作則會出發當前節點上所設置的數據監視以及父節點的子節點監視。一次成功的 delete() 操作將會觸發當前節點的數據監視和子節點監視事件,同時也會觸發該節點父節點的child watch。
Zookeeper 中的監視是輕量級的,因此容易設置、維護和分發。當客戶端與 Zookeeper 服務器端失去聯系時,客戶端并不會收到監視事件的通知,只有當客戶端重新連接后,若在必要的情況下,以前注冊的監視會重新被注冊并觸發,對于開發人員來說 這通常是透明的。只有一種情況會導致監視事件的丟失,即:通過 exists() 設置了某個 znode 節點的監視,但是如果某個客戶端在此 znode 節點被創建和刪除的時間間隔內與 zookeeper 服務器失去了聯系,該客戶端即使稍后重新連接 zookeeper服務器后也得不到事件通知。
Zookeeper C API 常量與部分結構(struct)介紹
與 ACL 相關的結構與常量:
struct Id 結構為:
struct Id {char * scheme;char * id; };struct ACL 結構為:
struct ACL {int32_t perms;struct Id id; };struct ACL_vector 結構為:
struct ACL_vector {int32_t count;struct ACL *data;};與 znode 訪問權限有關的常量
-
const int ZOO_PERM_READ; //允許客戶端讀取 znode 節點的值以及子節點列表。
-
const int ZOO_PERM_WRITE;// 允許客戶端設置 znode 節點的值。
-
const int ZOO_PERM_CREATE; //允許客戶端在該 znode 節點下創建子節點。
-
const int ZOO_PERM_DELETE;//允許客戶端刪除子節點。
-
const int ZOO_PERM_ADMIN; //允許客戶端執行 set_acl()。
-
const int ZOO_PERM_ALL;//允許客戶端執行所有操作,等價與上述所有標志的或(OR) 。
與 ACL IDs 相關的常量
-
struct Id ZOO_ANYONE_ID_UNSAFE; //(‘world’,’anyone’)
-
struct Id ZOO_AUTH_IDS;// (‘auth’,’’)
三種標準的 ACL
-
struct ACL_vector ZOO_OPEN_ACL_UNSAFE; //(ZOO_PERM_ALL,ZOO_ANYONE_ID_UNSAFE)
-
struct ACL_vector ZOO_READ_ACL_UNSAFE;// (ZOO_PERM_READ, ZOO_ANYONE_ID_UNSAFE)
-
struct ACL_vector ZOO_CREATOR_ALL_ACL; //(ZOO_PERM_ALL,ZOO_AUTH_IDS)
與 Interest 相關的常量:ZOOKEEPER_WRITE, ZOOKEEPER_READ
這 兩個常量用于標識感興趣的事件并通知 zookeeper 發生了哪些事件。Interest 常量可以進行組合或(OR)來標識多種興趣(multiple interests: write, read),這兩個常量一般用于 zookeeper_interest() 和 zookeeper_process()兩個函數中。
與節點創建相關的常量:ZOO_EPHEMERAL,?ZOO_SEQUENCE
zoo_create 函數標志,ZOO_EPHEMERAL 用來標識創建臨時節點,ZOO_SEQUENCE 用來標識節點命名具有遞增的后綴序號(一般是節點名稱后填充 10 位字符的序號,如 /xyz0000000000, /xyz0000000001, /xyz0000000002, ...),同樣地,ZOO_EPHEMERAL,?ZOO_SEQUENCE可以組合。
與連接狀態 Stat 相關的常量
以下常量均與 Zookeeper 連接狀態有關,他們通常用作監視器回調函數的參數。
| ZOOAPI const int? | ZOO_EXPIRED_SESSION_STATE |
| ZOOAPI const int? | ZOO_AUTH_FAILED_STATE |
| ZOOAPI const int? | ZOO_CONNECTING_STATE |
| ZOOAPI const int? | ZOO_ASSOCIATING_STATE |
| ZOOAPI const int? | ZOO_CONNECTED_STATE |
與監視類型(Watch Types)相關的常量
以下常量標識監視事件的類型,他們通常用作監視器回調函數的第一個參數。
- ZOO_CREATED_EVENT; // 節點被創建(此前該節點不存在),通過 zoo_exists() 設置監視。
- ZOO_DELETED_EVENT; // 節點被刪除,通過 zoo_exists() 和 zoo_get() 設置監視。
- ZOO_CHANGED_EVENT; // 節點發生變化,通過 zoo_exists() 和 zoo_get() 設置監視。
- ZOO_CHILD_EVENT; // 子節點事件,通過zoo_get_children() 和 zoo_get_children2()設置監視。
- ZOO_SESSION_EVENT; // 會話丟失
- ZOO_NOTWATCHING_EVENT; // 監視被移除。
Zookeeper C API 錯誤碼介紹 ZOO_ERRORS
| ZOK? | 正常返回 |
| ZSYSTEMERROR? | 系統或服務器端錯誤(System and server-side errors),服務器不會拋出該錯誤,該錯誤也只是用來標識錯誤范圍的,即大于該錯誤值,且小于 ZAPIERROR 都是系統錯誤。 |
| ZRUNTIMEINCONSISTENCY? | 運行時非一致性錯誤。 |
| ZDATAINCONSISTENCY? | 數據非一致性錯誤。 |
| ZCONNECTIONLOSS? | Zookeeper 客戶端與服務器端失去連接 |
| ZMARSHALLINGERROR? | 在 marshalling 和unmarshalling 數據時出現錯誤(Error while marshalling or unmarshalling data) |
| ZUNIMPLEMENTED? | 該操作未實現(Operation is unimplemented) |
| ZOPERATIONTIMEOUT? | 該操作超時(Operation timeout) |
| ZBADARGUMENTS? | 非法參數錯誤(Invalid arguments) |
| ZINVALIDSTATE? | 非法句柄狀態(Invliad zhandle state) |
| ZAPIERROR? | API 錯誤(API errors),服務器不會拋出該錯誤,該錯誤也只是用來標識錯誤范圍的,錯誤值大于該值的標識 API 錯誤,而小于該值的標識 ZSYSTEMERROR。 |
| ZNONODE? | 節點不存在(Node does not exist) |
| ZNOAUTH? | 沒有經過授權(Not authenticated) |
| ZBADVERSION? | 版本沖突(Version conflict) |
| ZNOCHILDRENFOREPHEMERALS? | 臨時節點不能擁有子節點(Ephemeral nodes may not have children) |
| ZNODEEXISTS? | 節點已經存在(The node already exists) |
| ZNOTEMPTY? | 該節點具有自身的子節點(The node has children) |
| ZSESSIONEXPIRED? | 會話過期(The session has been expired by the server) |
| ZINVALIDCALLBACK? | 非法的回調函數(Invalid callback specified) |
| ZINVALIDACL? | 非法的ACL(Invalid ACL specified) |
| ZAUTHFAILED? | 客戶端授權失敗(Client authentication failed) |
| ZCLOSING? | Zookeeper 連接關閉(ZooKeeper is closing) |
| ZNOTHING? | 并非錯誤,客戶端不需要處理服務器的響應(not error, no server responses to process) |
| ZSESSIONMOVED? | 會話轉移至其他服務器,所以操作被忽略(session moved to another server, so operation is ignored) |
?
至此,Zookeeper C API 中的大部分的常量和結構體均已介紹完畢,下一節《Zookeeper C API 指南三(回調函數)》將介紹 Zookeeper C API 中的回調函數。
接上一篇《Zookeeper C API 指南二(監視(Wathes), 基本常量和結構體介紹)》,本文重點介紹 Zookeeper C API 中的各種回調函數。
Zookeeper C API 中各種回調函數簡介
在具體介紹 Zookeeper C API 之前,首先介紹一下 Zookeeper C API 中的各種回調函數的原型:
監視函數(watch function)原型
typedef void (*watcher_fn)(zhandle_t *zh, int type, int state, const char *path,void *watcherCtx);?監視函數原型的各個參數解釋如下:
| zh | zookeeper 句柄(handle) |
| type | 事件類型(event type). *_EVENT 常量之一. |
| state | 連接狀態(connection state). 狀態值為 *_STATE 常量之一. |
| path | 觸發監視事件的 znode 節點的路徑,若為 NULL,則事件類型為 ZOO_SESSION_EVENT |
| watcherCtx | 監視器上下文(watcher context). |
其他回調函數的原型
Zookeeper 中還有幾種在異步 API(一般以 zoo_a*開頭的函數) 中使用的回調函數,根據回調函數處理異步函數返回值類型的不同分為以下幾類:處理返回 void 類型的回調函數,處理返回 Stat 結構的回調函數,處理返回字符串的回調函數,處理返回數據的回調函數,處理返回字符串列表(a list of string)的回調函數,同時處理返回字符串列表(a list of string)和 Stat 結構的回調函數,以及處理返回 ACL 信息的回調函數,它們分別如下:
// 處理返回 void 類型的回調函數 typedef void(* void_completion_t)(int rc, const void *data);// 處理返回 Stat 結構的回調函數 typedef void(* stat_completion_t)(int rc, const struct Stat *stat, const void *data); // 處理返回字符串的回調函數 typedef void(* string_completion_t)(int rc, const char *value, const void *data); // 處理返回數據的回調函數 typedef void(* data_completion_t)(int rc, const char *value, int value_len, const struct Stat *stat, const void *data); // 處理返回字符串列表(a list of string)的回調函數 typedef void(* strings_completion_t)(int rc, const struct String_vector *strings, const void *data); // 同時處理返回字符串列表(a list of string)和 Stat 結構的回調函數 typedef void(* strings_stat_completion_t)(int rc, const struct String_vector *strings, const struct Stat *stat, const void *data); // 處理以及返回 ACL 信息的回調函數 typedef void(* acl_completion_t)(int rc, struct ACL_vector *acl, struct Stat *stat, const void *data);?下面一一介紹上述幾種回調函數的用法:
- 處理返回 void 類型的回調函數
該回調函數一般在異步 API 調用結束或 Zookeeper? 客戶端失去連接時被調用。
| rc | 異步函數調用返回的錯誤碼,連接丟失/超時將觸發該原型函數(此處指具有該函數原型的回調函數,下同)的調用,并且錯誤碼為 ZCONNECTIONLOSS --? Zookeeper 客戶端與服務器端的連接丟失,或者 ZOPERATIONTIMEOUT -- 連接超時;而與數據相關的事件也會觸發該原型函數的調用,同時置相應的錯誤碼,具體見后文(0 代異步函數調用成功) |
| data | 由調用者傳入的指針,調用者可以通過該指針向回調函數傳入自定義的參數,開發人員應負責此指針所指向內存的釋放。 |
?
- 處理返回 Stat 結構的回調函數
該函數一般在異步 API 調用結束或 Zookeeper? 客戶端失去連接時被調用。
| rc | 異步函數調用返回的錯誤碼,連接丟失/超時將觸發該原型函數(此處指具有該函數原型的回調函數,下同)的調用,并且錯誤碼為 ZCONNECTIONLOSS --? Zookeeper 客戶端與服務器端的連接丟失,或者 ZOPERATIONTIMEOUT -- 連接超時;而與數據相關的事件也會觸發該原型函數的調用,同時置相應的錯誤碼,具體見后文(0 代異步函數調用成功) |
| stat | 指向與該 znode 節點相關的 Stat 信息,如果返回非 0 值(即異步調用函數出錯),stat 所指向的區域是未定義的,開發者不負責釋放 stat 所指向的內存空間。 |
| data | 由調用者傳入的指針,調用者可以通過該指針向回調函數傳入自定義的參數,開發人員應負責此指針所指向內存的釋放。 |
?
- 處理返回字符串的回調函數
該函數一般在異步 API 調用結束或 Zookeeper? 客戶端失去連接時被調用。
| rc | 異步函數調用返回的錯誤碼,連接丟失/超時將觸發該原型函數(此處指具有該函數原型的回調函數,下同)的調用,并且錯誤碼為 ZCONNECTIONLOSS --? Zookeeper 客戶端與服務器端的連接丟失,或者 ZOPERATIONTIMEOUT -- 連接超時;而與數據相關的事件也會觸發該原型函數的調用,同時置相應的錯誤碼,具體見后文(0 代異步函數調用成功) |
| value | 返回的字符串 |
| data | 由調用者傳入的指針,調用者可以通過該指針向回調函數傳入自定義的參數,開發人員應負責此指針所指向內存的釋放。 |
?
- 處理返回數據的回調函數
該函數一般在異步 API 調用結束或 Zookeeper? 客戶端失去連接時被調用。
| rc | 異步函數調用返回的錯誤碼,連接丟失/超時將觸發該原型函數(此處指具有該函數原型的回調函數,下同)的調用,并且錯誤碼為 ZCONNECTIONLOSS --? Zookeeper 客戶端與服務器端的連接丟失,或者 ZOPERATIONTIMEOUT -- 連接超時;而與數據相關的事件也會觸發該原型函數的調用,同時置相應的錯誤碼,具體見后文(0 代異步函數調用成功) |
| value | 異步調用的返回值,如果返回非 0 值(即異步調用函數出錯),value 所指向的區域是未定義的,開發者不負責釋放?value 所指向的內存空間。 |
| value_len | 返回 value 數據字節數(bytes) |
| stat | 指向與該 znode 節點相關的 Stat 信息,如果返回非 0 值(即異步調用函數出錯),stat 所指向的區域是未定義的,開發者不負責釋放 stat 所指向的內存空間。 |
| data | 由調用者傳入的指針,調用者可以通過該指針向回調函數傳入自定義的參數,開發人員應負責此指針所指向內存的釋放。 |
?
- 處理返回字符串列表(a list of string)的回調函數
該函數一般在異步 API 調用結束或 Zookeeper? 客戶端失去連接時被調用。
| rc | 異步函數調用返回的錯誤碼,連接丟失/超時將觸發該原型函數(此處指具有該函數原型的回調函數,下同)的調用,并且錯誤碼為 ZCONNECTIONLOSS --? Zookeeper 客戶端與服務器端的連接丟失,或者 ZOPERATIONTIMEOUT -- 連接超時;而與數據相關的事件也會觸發該原型函數的調用,同時置相應的錯誤碼,具體見后文(0 代異步函數調用成功) |
| strings | 指向包含了某 znode 節點的所有子節點名稱列表的結構,如果返回非 0 值(即異步調用函數出錯),strings 所指向的區域是未定義的,開發者不負責釋放strings 所指向的內存空間。 |
| data | 由調用者傳入的指針,調用者可以通過該指針向回調函數傳入自定義的參數,開發人員應負責此指針所指向內存的釋放。 |
?
- 同時處理返回字符串列表(a list of string)和 Stat 結構的回調函數
該函數一般在異步 API 調用結束或 Zookeeper? 客戶端失去連接時被調用。
| rc | 異步函數調用返回的錯誤碼,連接丟失/超時將觸發該原型函數(此處指具有該函數原型的回調函數,下同)的調用,并且錯誤碼為 ZCONNECTIONLOSS --? Zookeeper 客戶端與服務器端的連接丟失,或者 ZOPERATIONTIMEOUT -- 連接超時;而與數據相關的事件也會觸發該原型函數的調用,同時置相應的錯誤碼,具體見后文(0 代異步函數調用成功) |
| strings | 指向包含了某 znode 節點的所有子節點名稱列表的結構,如果返回非 0 值(即異步調用函數出錯),strings 所指向的區域是未定義的,開發者不負責釋放strings 所指向的內存空間。 |
| stat | 指向與該 znode 節點相關的 Stat 信息,如果返回非 0 值(即異步調用函數出錯),stat 所指向的區域是未定義的,開發者不負責釋放 stat 所指向的內存空間。 |
| data | 由調用者傳入的指針,調用者可以通過該指針向回調函數傳入自定義的參數,開發人員應負責此指針所指向內存的釋放。 |
?
- 處理返回 ACL 信息的回調函數
該函數一般在異步 API 調用結束或 Zookeeper? 客戶端失去連接時被調用。
| rc | 異步函數調用返回的錯誤碼,連接丟失/超時將觸發該原型函數(此處指具有該函數原型的回調函數,下同)的調用,并且錯誤碼為 ZCONNECTIONLOSS --? Zookeeper 客戶端與服務器端的連接丟失,或者 ZOPERATIONTIMEOUT -- 連接超時;而與數據相關的事件也會觸發該原型函數的調用,同時置相應的錯誤碼,具體見后文(0 代異步函數調用成功) |
| acl | 指向包含某 znode 節點 ACL 信息的指針,如果返回非 0 值(即異步調用函數出錯),acl 所指向的區域是未定義的,開發者不負責釋放 acl 所指向的內存空間。 |
| stat | 指向與該 znode 節點相關的 Stat 信息,如果返回非 0 值(即異步調用函數出錯),stat 所指向的區域是未定義的,開發者不負責釋放 stat 所指向的內存空間。 |
| data | 由調用者傳入的指針,調用者可以通過該指針向回調函數傳入自定義的參數,開發人員應負責此指針所指向內存的釋放。 |
?
至此,所有的回調函數均已介紹完畢,下一節將介紹 Zookeeper C API 分類和基本 API 的使用,見第四講《Zookeeper C API 指南四(C API 概覽)》
上一節《Zookeeper C API 指南三(回調函數)》重點講了 Zookeeper C API 中各種回調函數的原型,本節將切入正題,正式講解 Zookeeper C API。相信大家讀完本文后應該對 Zookeeper C API 的使用有一個比較清晰的認識。
Zookeeper C API 概覽
Zookeeper C API 很規范,接口很容易記憶,大部分接口均以 zoo_ 開頭,只有少量接口以 zookeeper_ 開頭,所有的 API 匯總如下:
void zoo_create_op_init(zoo_op_t * op, const char *path, const char *value,int valuelen, const struct ACL_vector *acl,int flags, char *path_buffer, int path_buffer_len);void zoo_delete_op_init(zoo_op_t * op, const char *path, int version);void zoo_set_op_init(zoo_op_t * op, const char *path,const char *buffer, int buflen, int version,struct Stat *stat);void zoo_check_op_init(zoo_op_t * op, const char *path, int version);ZOOAPI zhandle_t *zookeeper_init(const char *host, watcher_fn fn,int recv_timeout,const clientid_t * clientid,void *context, int flags);ZOOAPI int zookeeper_close(zhandle_t * zh);ZOOAPI const clientid_t *zoo_client_id(zhandle_t * zh);ZOOAPI int zoo_recv_timeout(zhandle_t * zh);ZOOAPI const void *zoo_get_context(zhandle_t * zh);ZOOAPI void zoo_set_context(zhandle_t * zh, void *context);ZOOAPI watcher_fn zoo_set_watcher(zhandle_t * zh, watcher_fn newFn);ZOOAPI struct sockaddr *zookeeper_get_connected_host(zhandle_t * zh, struct sockaddr*addr,socklen_t * addr_len);ZOOAPI int zookeeper_interest(zhandle_t * zh, int *fd, int *interest,struct timeval *tv);ZOOAPI int zookeeper_process(zhandle_t * zh, int events);ZOOAPI int zoo_state(zhandle_t * zh);ZOOAPI int zoo_acreate(zhandle_t * zh, const char *path,const char *value, int valuelen,const struct ACL_vector *acl, int flags,string_completion_t completion, const void *data);ZOOAPI int zoo_adelete(zhandle_t * zh, const char *path, int version,void_completion_t completion, const void *data);ZOOAPI int zoo_aexists(zhandle_t * zh, const char *path, int watch,stat_completion_t completion, const void *data);ZOOAPI int zoo_awexists(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,stat_completion_t completion, const void *data);ZOOAPI int zoo_aget(zhandle_t * zh, const char *path, int watch,data_completion_t completion, const void *data);ZOOAPI int zoo_awget(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,data_completion_t completion, const void *data);ZOOAPI int zoo_aset(zhandle_t * zh, const char *path,const char *buffer, int buflen, int version,stat_completion_t completion, const void *data);ZOOAPI int zoo_aget_children(zhandle_t * zh, const char *path,int watch,strings_completion_t completion,const void *data);ZOOAPI int zoo_awget_children(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,strings_completion_t completion,const void *data);ZOOAPI int zoo_aget_children2(zhandle_t * zh, const char *path,int watch,strings_stat_completion_t completion,const void *data);ZOOAPI int zoo_awget_children2(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,strings_stat_completion_t completion,const void *data);ZOOAPI int zoo_async(zhandle_t * zh, const char *path,string_completion_t completion, const void *data);ZOOAPI int zoo_aget_acl(zhandle_t * zh, const char *path,acl_completion_t completion, const void *data);ZOOAPI int zoo_aset_acl(zhandle_t * zh, const char *path, int version,struct ACL_vector *acl, void_completion_t,const void *data);ZOOAPI int zoo_amulti(zhandle_t * zh, int count, const zoo_op_t * ops,zoo_op_result_t * results, void_completion_t,const void *data);ZOOAPI const char *zerror(int c);ZOOAPI int zoo_add_auth(zhandle_t * zh, const char *scheme,const char *cert, int certLen,void_completion_t completion, const void *data);ZOOAPI int is_unrecoverable(zhandle_t * zh);ZOOAPI void zoo_set_debug_level(ZooLogLevel logLevel);ZOOAPI void zoo_set_log_stream(FILE * logStream);ZOOAPI void zoo_deterministic_conn_order(int yesOrNo);ZOOAPI int zoo_create(zhandle_t * zh, const char *path,const char *value, int valuelen,const struct ACL_vector *acl, int flags,char *path_buffer, int path_buffer_len);ZOOAPI int zoo_delete(zhandle_t * zh, const char *path, int version);ZOOAPI int zoo_exists(zhandle_t * zh, const char *path, int watch,struct Stat *stat);ZOOAPI int zoo_wexists(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,struct Stat *stat);ZOOAPI int zoo_get(zhandle_t * zh, const char *path, int watch,char *buffer, int *buffer_len, struct Stat *stat);ZOOAPI int zoo_wget(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,char *buffer, int *buffer_len, struct Stat *stat);ZOOAPI int zoo_set(zhandle_t * zh, const char *path,const char *buffer, int buflen, int version);ZOOAPI int zoo_set2(zhandle_t * zh, const char *path,const char *buffer, int buflen, int version,struct Stat *stat);ZOOAPI int zoo_get_children(zhandle_t * zh, const char *path,int watch, struct String_vector *strings);ZOOAPI int zoo_wget_children(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,struct String_vector *strings);ZOOAPI int zoo_get_children2(zhandle_t * zh, const char *path,int watch, struct String_vector *strings,struct Stat *stat);ZOOAPI int zoo_wget_children2(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,struct String_vector *strings,struct Stat *stat);ZOOAPI int zoo_get_acl(zhandle_t * zh, const char *path,struct ACL_vector *acl, struct Stat *stat);ZOOAPI int zoo_set_acl(zhandle_t * zh, const char *path, int version,const struct ACL_vector *acl);ZOOAPI int zoo_multi(zhandle_t * zh, int count, const zoo_op_t * ops,zoo_op_result_t * results);除了基本的初始化、銷毀 Zookeeper 句柄(zhandle),設置日志等級、日志流以及一些具有輔助功能 API(zerror(), zoo_state()等) 外,Zookeeper C API 大部分接口可以根據同步和異步特性分為兩類,同步接口以 zoo_* 開頭,異步接口以則以 zoo_a* 開頭。并且除了 zookeeper_init() 以及與 zoo_multi() 或 zoo_amulti() 批量操作相關的 zoo_op_t 初始化外,其他的 API 的第一個參數均為 zhandle_t * zh, 即 Zookeeper 句柄的指針。
Zookeeper C API 分類
- 初始化、銷毀 Zookeeper 句柄
- 輔助函數
- 與 zoo_multi() 和 zoo_amulti() 批量操作相關的 zoo_op_t 初始化
- Zookeeper C API 同步接口
- Zookeeper C API 異步接口
?Zookeeper C API 初體驗
有了上面的介紹,下面我們來看一看如何使簡單地使用 Zookeeper C API 吧。
在使用 Zookeeper C API 時應注意:
- 頭文件包含 #include <zookeeper/zookeeper.h>
- 如果你需要編譯多線程版本客戶端程序,請添加編譯選項 -DTHREADED,同時鏈接時應鏈接?zookeeper_mt 庫;如果你需要編譯單線程客戶端程序,請不要添加編譯選項 -DTHREADED,同時鏈接時應鏈接?zookeeper_st 庫。
一個基本的程序如下(更詳細的例子可以參看 src/c/src/cli.c):
/** =============================================================================** Filename: zktest.c** Description: zookeeper api testcase.** Created: 02/15/2013 08:48:49 PM** Author: Fu Haiping (forhappy), haipingf@gmail.com* Company: ICT ( Institute Of Computing Technology, CAS )** =============================================================================*/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <zookeeper/zookeeper.h> #include <zookeeper/zookeeper_log.h>void zktest_watcher_g(zhandle_t* zh, int type, int state,const char* path, void* watcherCtx) {printf("Something happened.\n");printf("type: %d\n", type);printf("state: %d\n", state);printf("path: %s\n", path);printf("watcherCtx: %s\n", (char *)watcherCtx); }void zktest_dump_stat(const struct Stat *stat) {char tctimes[40];char tmtimes[40];time_t tctime;time_t tmtime;if (!stat) {fprintf(stderr,"null\n");return;}tctime = stat->ctime/1000;tmtime = stat->mtime/1000;ctime_r(&tmtime, tmtimes);ctime_r(&tctime, tctimes);fprintf(stderr, "\tctime = %s\tczxid=%llx\n""\tmtime=%s\tmzxid=%llx\n""\tversion=%x\taversion=%x\n""\tephemeralOwner = %llx\n",tctimes, stat->czxid,tmtimes, stat->mzxid,(unsigned int)stat->version, (unsigned int)stat->aversion,stat->ephemeralOwner); }void zktest_stat_completion(int rc, const struct Stat *stat, const void *data) {fprintf(stderr, "%s: rc = %d Stat:\n", (char*)data, rc);zktest_dump_stat(stat); }void zktest_void_completion(int rc, const void *data) {fprintf(stderr, "[%s]: rc = %d\n", (char*)(data==0?"null":data), rc); }void zktest_string_completion(int rc, const char *name, const void *data) {fprintf(stderr, "[%s]: rc = %d\n", (char*)(data==0?"null":data), rc);if (!rc) {fprintf(stderr, "\tname = %s\n", name);} }int main(int argc, const char *argv[]) {const char* host = "127.0.0.1:2181,127.0.0.1:2182,""127.0.0.1:2183,127.0.0.1:2184,127.0.0.1:2185";int timeout = 30000;zoo_set_debug_level(ZOO_LOG_LEVEL_WARN);zhandle_t* zkhandle = zookeeper_init(host,zktest_watcher_g, timeout, 0, "hello zookeeper.", 0);if (zkhandle == NULL) {fprintf(stderr, "Error when connecting to zookeeper servers...\n");exit(EXIT_FAILURE);}// struct ACL ALL_ACL[] = {{ZOO_PERM_ALL, ZOO_ANYONE_ID_UNSAFE}};// struct ACL_vector ALL_PERMS = {1, ALL_ACL};int ret = zoo_acreate(zkhandle, "/xyz", "hello", 5,&ZOO_OPEN_ACL_UNSAFE, 0 /* ZOO_SEQUENCE */,zktest_string_completion, "acreate");if (ret) {fprintf(stderr, "Error %d for %s\n", ret, "acreate");exit(EXIT_FAILURE);}ret = 0;ret = zoo_aexists(zkhandle, "/xyz", 1, zktest_stat_completion, "aexists");if (ret) {fprintf(stderr, "Error %d for %s\n", ret, "aexists");exit(EXIT_FAILURE);}ret = 0;// Wait for asynchronous zookeeper call done. getchar();ret = zoo_adelete(zkhandle, "/xyz", -1, zktest_void_completion, "adelete");if (ret) {fprintf(stderr, "Error %d for %s\n", ret, "adelete");exit(EXIT_FAILURE);}// Wait for asynchronous zookeeper call done. getchar();zookeeper_close(zkhandle); }下面簡單講講這個程序的結構:
- 首先聲明 host, timeout 變量。
其中 host 字符串格式為逗號隔開的 IP:PORT對,可以是 Zookeeper 集群中的全部或部分 Zookeeper 實例的 IP:PORT對,我們在第一講《準備工作》中介紹了如何部署一個偽分布式的集群,上述的 host 就是這些 zookeeper 實例的IP:PORT對。
另外 timeout 是 Zookeeper 客戶端連接服務器的超時時間,單位為毫秒,timeout = 30000 說明如果 30 秒內客戶端沒有連接上 Zookeeper 服務則表示連接超時。
- 設置日志等級。
- 初始化 Zookeeper 句柄(zhandle_t)。
初始化 Zookeeper 句柄 zookeeper_init() 函數原型如下:
ZOOAPI zhandle_t *zookeeper_init(const char *host, watcher_fn fn,int recv_timeout,const clientid_t * clientid,void *context, int flags);各個參數解釋如下:
| host | 逗號隔開的 host:port 對, 每個代表一個 zk server, 例如: "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002" |
| fn | 全局的監視器回調函數,當發生事件通知時,該函數會被調用。 |
| clientid | 客戶端嘗試重連的先前會話的ID,如果不需要重連先前的會話,則設置為 0??蛻舳丝梢酝ㄟ^調用 zoo_client_id來訪問一個已經連接上的并且有效的會話ID,如果clientid對應的會話超時,或者由于某種原因 clientid變為無效了,那么zookeeper_init 將返回一個非法的zhandle_t, 通過 zhandle_t 的狀態可以獲知zookeeper_init 調用失敗的原因。 (通常為 ZOO_EXPIRED_SESSION_STATE). |
| context | 與 zhandle_t 實例相關聯的“上下文對象”(可以通過該參數為 zhandle_t 傳入自定義類型的數據),應用程序可以通過 zoo_get_context訪問它(例如在監視器回調函數中),當然 zookeeper 內部沒有用到該參數,所以 context 可以設置為 NULL。 |
| flags | 目前為保留參數,設置為 0。 |
- 創建一個 znode 節點。
這里采用異步方式創建 znode 節點,zoo_acreate() 函數原型為:
ZOOAPI int zoo_acreate(zhandle_t * zh, const char *path,const char *value, int valuelen,const struct ACL_vector *acl, int flags,string_completion_t completion, const void *data);各個參數解釋如下:
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| value | 該節點保存的數據。 |
| valuelen | 該節點保存數據的大小。 |
| acl | 該節點初始 ACL,ACL 不能為null 或空。 |
| flags | 該參數可以設置為 0,或者創建標識符 ZOO_EPHEMERAL,?ZOO_SEQUENCE 的組合或(OR)。 |
| completion | 當創建節點請求完成時會調用該函數,該函數原型詳見第三講《回調函數》一節。同時傳遞給completion的rc參數為: ZOK 操作完成;ZNONODE 父節點不存在;ZNODEEXISTS 節點已存在;ZNOAUTH 客戶端沒有權限創建節點。ZNOCHILDRENFOREPHEMERALS 臨時節點不能創建子節點。 |
| data | completino函數被調用時,傳遞給 completion 的數據。 |
- 調用 exists() 函數,設置監視器。
- 調用 delete 函數,刪除 znode 節點。
- 銷毀 zookeeper 句柄
好了,至此本文大致講完了 Zookeeper C API 的分類和幾個基本函數的用法。之所以為 Zookeeper C API 分類是方便記憶,開發者可以迅速找到自己需要的 API;另外,本文還講了幾個基本函數的使用方法,包括 zookeeper_init(),zoo_acreate(), zoo_aexists(), zoo_adelete() 以及 zookeeper_close()。相信大家對 Zookeeper C API 也有了一個大致的了解,第五講我會給大家介紹 Zookeeper C API 中的同步調用的函數(即以 zoo_* 開頭的函數),然后第六講給大家介紹 Zookeeper C API 中的異步調用的函數(即以 zoo_a* 開頭的函數)。
上一講《Zookeeper C API 指南四(C API 概覽)》講了Zookeeper C API 的分類和幾個基本函數的用法,相信大家對 Zookeeper C API 也有了一個大致的了解,本文我會給大家介紹 Zookeeper C API 中的同步調用的函數(即以 zoo_* 開頭的函數)。
Zookeeper C API 中與訪問 Zookeeper 服務相關(比如創建、刪除 znode 節點,獲取子節點,設置 znode 數據等)的同步 API 如下:
ZOOAPI int zoo_add_auth(zhandle_t * zh, const char *scheme,const char *cert, int certLen,void_completion_t completion, const void *data);ZOOAPI int zoo_create(zhandle_t * zh, const char *path,const char *value, int valuelen,const struct ACL_vector *acl, int flags,char *path_buffer, int path_buffer_len);ZOOAPI int zoo_delete(zhandle_t * zh, const char *path, int version);ZOOAPI int zoo_exists(zhandle_t * zh, const char *path, int watch,struct Stat *stat);ZOOAPI int zoo_wexists(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,struct Stat *stat);ZOOAPI int zoo_get(zhandle_t * zh, const char *path, int watch,char *buffer, int *buffer_len, struct Stat *stat);ZOOAPI int zoo_wget(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,char *buffer, int *buffer_len, struct Stat *stat);ZOOAPI int zoo_set(zhandle_t * zh, const char *path,const char *buffer, int buflen, int version);ZOOAPI int zoo_set2(zhandle_t * zh, const char *path,const char *buffer, int buflen, int version,struct Stat *stat);ZOOAPI int zoo_get_children(zhandle_t * zh, const char *path,int watch, struct String_vector *strings);ZOOAPI int zoo_wget_children(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,struct String_vector *strings);ZOOAPI int zoo_get_children2(zhandle_t * zh, const char *path,int watch, struct String_vector *strings,struct Stat *stat);ZOOAPI int zoo_wget_children2(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,struct String_vector *strings,struct Stat *stat);ZOOAPI int zoo_get_acl(zhandle_t * zh, const char *path,struct ACL_vector *acl, struct Stat *stat);ZOOAPI int zoo_set_acl(zhandle_t * zh, const char *path, int version,const struct ACL_vector *acl);ZOOAPI int zoo_multi(zhandle_t * zh, int count, const zoo_op_t * ops,zoo_op_result_t * results);本文將以上同步 API 在此細分為一下幾類:(1). 創建、刪除 znode 節點,(2). 可設置 watch 的 API,(3). 訪問、設置節點 ACL 的 API,(4). 批處理 API
- 創建、刪除 znode 節點(兩個)
- 創建 znode 節點:
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| value | 該節點保存的數據。 |
| valuelen | 該節點保存數據的大小。如果 value 被設置為 NULL(該 znode 節點不包含數據),則 valuelen 應該設置為 -1。 |
| acl | 該節點初始 ACL,ACL 不能為null 或空。 |
| flags | 該參數可以設置為 0,或者創建標識符ZOO_EPHEMERAL,?ZOO_SEQUENCE 的組合或(OR)。 |
| path_buffer | 用于保存返回節點新路徑(因為設置了ZOO_SEQUENCE 后 zoo_create 所創建的節點名稱與參數 path 提供的名稱不同,新的節點名稱后面填充了序號),path 字符串以 NULL 結束。path_buffer 可以設置為 NULL,此時 path_buffer_len 等于 0。 |
| path_buffer_len | path_buffer 的長度,如果新節點名稱的長度大于path_buffer_len,則節點名稱將會被截斷,而服務器端該節點的名稱不會截斷。 |
- 刪除 znode 節點:
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| version | 節點的版本號,如果該 znode 節點的實際版本號與該參數提供的版本號不一值,則刪除節點失敗,如果 version 為 -1,則不作版本檢查。 |
?
- 可設置 watch 的 API(exists(兩個) + get(兩個) + get_children(四個) = 八個)
- 檢查節點狀態 exists(兩個,分別是 zoo_exists() 和 zoo_wexists(),區別是后者可以指定單獨的 watcher_fn(監視器回調函數),而前者只能用 zookeeper_init() 設置的全局監視器回調函數,同時 get 和 get_children兩族函數也一樣,帶有zoo_w* 的函數可以指定單獨的 watcher_fn)。
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watch | 如果非 0,則在服務器端設置監視,當節點發生變化時客戶端會得到通知,即使當前指定的節點不存在也會設置監視,這樣該節點被創建時,客戶端也可以得到通知。 |
| stat | 返回的 Stat 信息。 |
?
ZOOAPI int zoo_wexists(zhandle_t * zh, const char *path, watcher_fn watcher, void *watcherCtx,struct Stat *stat);| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watcher | 如果不為 NULL 則會在服務器端設置監視,當節點發生變化時客戶端會得到通知,即使當前指定的節點不存在也會設置監視,這樣該節點被創建時,客戶端也可以得到通知。 |
| watcherCtx | 用戶指定的數據,將被傳入到監視器回調函數中,與由 zookeeper_init() 設置的全局監視器上下文不同,該函數設置的監視器上下文只與當前的監視器相關聯。 |
| stat | 返回的 Stat 信息。 |
?
- 獲取節點數據 get(兩個)
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watch | 如果非0,則在服務器端設置監視,當節點發生變化時客戶端會得到通知。 |
| buffer | 用于保存從 zookeeper 服務器獲取的節點數據。 |
| buffer_len | buffer 大小,一旦成功返回該值將會被設置為節點數據的實際大小,如果節點的數據為空,則數據大小為 -1,buffer_len 也為 -1。 |
| stat | 如果非空,stat 指向的結構將會保存該節點的 Stat 信息。 |
?
ZOOAPI int zoo_wget(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,char *buffer, int *buffer_len, struct Stat *stat);| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watcher | 如果不為 NULL 則會在服務器端設置監視,當節點發生變化時客戶端會得到通知。 |
| watcherCtx | 用戶指定的數據,將被傳入到監視器回調函數中,與由 zookeeper_init() 設置的全局監視器上下文不同,該函數設置的監視器上下文只與當前的監視器相關聯。 |
| buffer | 用于保存從 zookeeper 服務器獲取的節點數據。 |
| buffer_len | buffer 大小,一旦成功返回該值將會被設置為節點數據的實際大小,如果節點的數據為空,則數據大小為 -1,buffer_len 也為 -1。 |
| stat | 如果非空,stat 指向的結構將會保存該節點的 Stat 信息。 |
- 獲取子節點列表 get_children(四個)
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watch | 如果非0,則在服務器端設置監視,當節點發生變化時客戶端會得到通知。 |
| strings | 返回各個子節點路徑 |
?
ZOOAPI int zoo_wget_children(zhandle_t * zh, const char *path, watcher_fn watcher, void *watcherCtx,struct String_vector *strings);| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watcher | 如果不為 NULL 則會在服務器端設置監視,當節點發生變化時客戶端會得到通知。 |
| watcherCtx | 用戶指定的數據,將被傳入到監視器回調函數中,與由 zookeeper_init() 設置的全局監視器上下文不同,該函數設置的監視器上下文只與當前的監視器相關聯。 |
| strings | 回各個子節點路徑 |
?
ZOOAPI int zoo_get_children2(zhandle_t * zh, const char *path, int watch, struct String_vector *strings,struct Stat *stat);該函數最早出現在 Zookeeper 3.3.0中,該函數功能與 zoo_get_children() 基本一致,但同時還會返回指定節點的 Stat 信息。
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watch | 如果非0,則在服務器端設置監視,當節點發生變化時客戶端會得到通知。 |
| strings | 返回各個子節點路徑。 |
| stat | 返回指定節點的 Stat 信息。 |
?
ZOOAPI int zoo_wget_children2(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,struct String_vector *strings,struct Stat *stat);該函數最早出現在 Zookeeper 3.3.0中,該函數功能與 zoo_wget_children() 基本一致,但同時還會返回指定節點的 Stat 信息。
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watcher | 如果不為 NULL 則會在服務器端設置監視,當節點發生變化時客戶端會得到通知。 |
| watcherCtx | 用戶指定的數據,將被傳入到監視器回調函數中,與由 zookeeper_init() 設置的全局監視器上下文不同,該函數設置的監視器上下文只與當前的監視器相關聯。 |
| strings | 返回各個子節點路徑。 |
| stat | 返回指定節點的 Stat 信息。 |
- 訪問、設置節點 ACL (兩個)
- 獲取節點 ACL 信息
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| acl | 該函數所返回的指定節點的 ACL 信息。 |
| stat | 返回指定節點的 Stat 信息。 |
- 設置節點 ACL
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| version | 節點的版本號。 |
| acl | 需要設置的 ACL 信息。 |
- 批處理,即原子性地一次提交多個 Zookeeper 操作。
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| count | 提交操作的個數。 |
| ops | 包含所提交操作數組。 |
| results | 包含操作所返回結果的數組。 |
其中 zoo_op_t 是各種操作(創建、刪除節點,設置節點數據和檢查節點狀態四種操作)一個封裝(聯合體),定義如下:
typedef struct zoo_op {int type;union {// CREATEstruct {const char *path;const char *data;int datalen;char *buf;int buflen;const struct ACL_vector *acl;int flags;} create_op;// DELETE struct {const char *path;int version;} delete_op;// SETstruct {const char *path;const char *data;int datalen;int version;struct Stat *stat;} set_op;// CHECKstruct {const char *path;int version;} check_op;}; } zoo_op_t;zoo_op_t 一般由以下四個函數初始化:
void zoo_create_op_init(zoo_op_t * op, const char *path, const char *value,int valuelen, const struct ACL_vector *acl,int flags, char *path_buffer, int path_buffer_len);void zoo_delete_op_init(zoo_op_t * op, const char *path, int version);void zoo_set_op_init(zoo_op_t * op, const char *path,const char *buffer, int buflen, int version,struct Stat *stat);void zoo_check_op_init(zoo_op_t * op, const char *path, int version);zoo_op_result_t 用于保存 zoo_multi 或者 zoo_amulti 返回的其中一個結果,定義如下:
typedef struct zoo_op_result {int err;char *value;int valuelen;struct Stat *stat; } zoo_op_result_t;?
以上內容是 Zookeeper? C API 中同步 API 的介紹,如有錯誤請留下您的想法和意見,本人會盡快更正;同時,我也將在后面的文章中列舉一些示例來說明上述 API 的用法,如有興趣請繼續關注,如果您需要了解 Zookeeper 異步 API,請移步第六講《Zookeeper C API 指南六(異步 API 介紹)》。
上一講《Zookeeper C API 指南五(同步 API 介紹)》講了Zookeeper 同步 API 的分類和相關解釋,相信大家對 Zookeeper 同步 API 也有了一個大致的了解,本文我會給大家介紹 Zookeeper C API 中的異步調用的函數(即以 zoo_a* 開頭的函數),本文大致結構與《Zookeeper C API 指南五(同步 API 介紹)》,先匯總 API,然后再分類,并對每個 API 作出解釋。
在具體講解 Zookeeper 異步 API 之前,首先回顧一下《Zookeeper C API 指南三(回調函數)》,除了監視器回調函數以外,還有其他 7 種回調函數,他們通常在異步 API 調用結束或 Zookeeper? 客戶端失去連接時被調用。根據回調函數返回參數(即函數的輸出參數)的類型不同分為以下幾類:返回 void 類型的回調函數,返回 Stat 結構的回調函數,返回字符串的回調函數,返回數據的回調函數,返回字符串列表(a list of string)的回調函數,同時返回字符串列表(a list of string)和 Stat 結構的回調函數,以及返回 ACL 信息的回調函數,7 中回調函數原型聲明如下:
// 返回 void 類型的回調函數 typedef void(* void_completion_t)(int rc, const void *data);// 返回 Stat 結構的回調函數 typedef void(* stat_completion_t)(int rc, const struct Stat *stat, const void *data); // 返回字符串的回調函數 typedef void(* string_completion_t)(int rc, const char *value, const void *data); // 返回數據的回調函數 typedef void(* data_completion_t)(int rc, const char *value, int value_len, const struct Stat *stat, const void *data); // 返回字符串列表(a list of string)的回調函數 typedef void(* strings_completion_t)(int rc, const struct String_vector *strings, const void *data); // 同時返回字符串列表(a list of string)和 Stat 結構的回調函數 typedef void(* strings_stat_completion_t)(int rc, const struct String_vector *strings, const struct Stat *stat, const void *data); // 以及返回 ACL 信息的回調函數 typedef void(* acl_completion_t)(int rc, struct ACL_vector *acl, struct Stat *stat, const void *data);?可能這么說還不是很理解,那么我們以異步創建 znode 節點(zoo_acreate())為例解釋一下:
zoo_acreate函數原型如下:
ZOOAPI int zoo_acreate(zhandle_t * zh, const char *path,const char *value, int valuelen,const struct ACL_vector *acl, int flags,string_completion_t completion, const void *data);其中參數 string_completion_t completion 即返回字符串的回調函數,那么當 zoo_acreate 調用結束時將會觸發 completion 回調函數的調用,同時傳遞給 completion 的rc 參數為: ZOK 操作完成;ZNONODE 父節點不存在;ZNODEEXISTS 節點已存在;ZNOAUTH 客戶端沒有權限創建節點。ZNOCHILDRENFOREPHEMERALS 臨時節點不能創建子節點。而string_completion_t completion 中 const char *value 參數即新節點的路徑名(注:如果 zoo_acreate 設置了ZOO_EPHEMERAL,則創建節點成功后,節點名稱并不是 zoo_acreate 中 path 參數所指定的名稱,而是類似與 /xyz0000000001,/xyz0000000002... 的名稱)。另外,string_completion_t completion 中 const void *data 參數即為 zoo_acreate 中的 const void *data。
一般來說,zoo_acreate 函數可以按照以下方式調用:
int ret = zoo_acreate(zkhandle, "/xyz", "hello", 5,&ZOO_OPEN_ACL_UNSAFE, 0 /* ZOO_SEQUENCE */,zktest_string_completion, "acreate");if (ret) {fprintf(stderr, "Error %d for %s\n", ret, "acreate");exit(EXIT_FAILURE);}?其中 zktest_string_completion 功能很簡單,就是把創建成功后的節點名稱打印出來,函數定義如下:
void zktest_string_completion(int rc, const char *name, const void *data) {fprintf(stderr, "[%s]: rc = %d\n", (char*)(data==0?"null":data), rc);if (!rc) {fprintf(stderr, "\tname = %s\n", name);} }?好了,有了上面的基礎,我們接下來再來講講 Zookeeper 異步 API 吧 :-)
Zookeeper C API 中與訪問 Zookeeper 服務相關(比如創建、刪除 znode 節點,獲取子節點,設置 znode 數據等)的異步 API 如下:
ZOOAPI int zoo_acreate(zhandle_t * zh, const char *path,const char *value, int valuelen,const struct ACL_vector *acl, int flags,string_completion_t completion, const void *data);ZOOAPI int zoo_adelete(zhandle_t * zh, const char *path, int version,void_completion_t completion, const void *data);ZOOAPI int zoo_aexists(zhandle_t * zh, const char *path, int watch,stat_completion_t completion, const void *data);ZOOAPI int zoo_awexists(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,stat_completion_t completion, const void *data);ZOOAPI int zoo_aget(zhandle_t * zh, const char *path, int watch,data_completion_t completion, const void *data);ZOOAPI int zoo_awget(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,data_completion_t completion, const void *data);ZOOAPI int zoo_aset(zhandle_t * zh, const char *path,const char *buffer, int buflen, int version,stat_completion_t completion, const void *data);ZOOAPI int zoo_aget_children(zhandle_t * zh, const char *path,int watch,strings_completion_t completion,const void *data);ZOOAPI int zoo_awget_children(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,strings_completion_t completion,const void *data);ZOOAPI int zoo_aget_children2(zhandle_t * zh, const char *path,int watch,strings_stat_completion_t completion,const void *data);ZOOAPI int zoo_awget_children2(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,strings_stat_completion_t completion,const void *data);ZOOAPI int zoo_async(zhandle_t * zh, const char *path,string_completion_t completion, const void *data);ZOOAPI int zoo_aget_acl(zhandle_t * zh, const char *path,acl_completion_t completion, const void *data);ZOOAPI int zoo_aset_acl(zhandle_t * zh, const char *path, int version,struct ACL_vector *acl, void_completion_t,const void *data);ZOOAPI int zoo_amulti(zhandle_t * zh, int count, const zoo_op_t * ops,zoo_op_result_t * results, void_completion_t,const void *data);本文將以上異步 API 細分為以下幾類:(1). 創建、刪除 znode 節點,(2). 可設置 watch 的 API,(3). 訪問、設置節點 ACL 的 API,(4). 異步批處理 API。
- ?創建、刪除 znode 節點
- 創建 znode 節點
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| value | 該節點保存的數據。 |
| valuelen | 該節點保存數據的大小。 |
| acl | 該節點初始 ACL,ACL 不能為null 或空。 |
| flags | 該參數可以設置為 0,或者創建標識符 ZOO_EPHEMERAL,?ZOO_SEQUENCE 的組合或(OR)。 |
| completion | 當創建節點請求完成時會調用該函數,該函數原型詳見第三講《回調函數》一節。同時傳遞給completion的rc參數為: ZOK 操作完成;ZNONODE 父節點不存在;ZNODEEXISTS 節點已存在;ZNOAUTH 客戶端沒有權限創建節點。ZNOCHILDRENFOREPHEMERALS 臨時節點不能創建子節點。 |
| data | completion 函數被調用時,傳遞給 completion 的數據。 |
- 刪除 znode 節點
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| version | 期望的節點版本號,如果真實的版本號與期望的版本號不同則 zoo_delete() 調用失敗,-1 表示不不檢查版本號。 |
| completion | 當刪除節點請求完成時會調用該函數,該函數原型詳見第三講《回調函數》一節。同時傳遞給completion的rc參數為: ZOK 操作完成;ZNONODE 節點不存在;ZNOAUTH 客戶端沒有權限刪除節點;ZBADVERSION 版包號不匹配;ZNOTEMPTY 當前節點存在子節點,不能被刪除。 |
| data | completion 函數被調用時,傳遞給 completion 的數據。 |
?
- 可設置 watch 的 API(exists(兩個) + get(兩個) + get_children(四個) = 八個)
- 檢查節點狀態 exists(兩個,分別是 zoo_aexists() 和 zoo_awexists(),區別是后者可以指定單獨的 watcher_fn(監視器回調函數),而前者只能用 zookeeper_init() 設置的全局監視器回調函數,同時 aget 和 aget_children兩族函數也一樣,帶有zoo_w* 的函數可以指定單獨的 watcher_fn)。)
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watch | 如果非 0,則在服務器端設置監視,當節點發生變化時客戶端會得到通知,即使當前指定的節點不存在也會設置監視,這樣該節點被創建時,客戶端也可以得到通知。 |
| completion | 當 zoo_aexists 請求完成時會調用該函數,該函數原型詳見第三講《回調函數》一節。同時傳遞給completion的rc參數為: ZOK 操作完成;ZNONODE 節點不存在;ZNOAUTH 客戶端沒有權限刪除節點。 |
| data | completion 函數被調用時,傳遞給 completion 的數據。 |
?
ZOOAPI int zoo_awexists(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,stat_completion_t completion, const void *data);| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watcher | 如果非 0,則在服務器端設置監視,當節點發生變化時客戶端會得到通知,即使當前指定的節點不存在也會設置監視,這樣該節點被創建時,客戶端也可以得到通知。 |
| watcherCtx | 用戶指定的數據,將被傳入到監視器回調函數中,與由 zookeeper_init() 設置的全局監視器上下文不同,該函數設置的監視器上下文只與當前的監視器相關聯。 |
| completion | 當zoo_awexists 請求完成時會調用該函數,該函數原型詳見第三講《回調函數》一節。同時傳遞給completion的rc參數為: ZOK 操作完成;ZNONODE 節點不存在;ZNOAUTH 客戶端沒有權限刪除節點。 |
| data | completion 函數被調用時,傳遞給 completion 的數據。 |
?
- 獲取節點數據 aget(兩個)
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watch | 如果非 0,則在服務器端設置監視,當節點發生變化時客戶端會得到通知。 |
| completion | 當zoo_aget 請求完成時會調用該函數,該函數原型詳見第三講《回調函數》一節。同時傳遞給completion的rc參數為: ZOK 操作完成;ZNONODE 節點不存在;ZNOAUTH 客戶端沒有權限刪除節點。 |
| data | completion 函數被調用時,傳遞給 completion 的數據。 |
?
ZOOAPI int zoo_awget(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,data_completion_t completion, const void *data);| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watcher | 如果非 0,則在服務器端設置監視,當節點發生變化時客戶端會得到通知,即使當前指定的節點不存在也會設置監視,這樣該節點被創建時,客戶端也可以得到通知。 |
| watcherCtx | 用戶指定的數據,將被傳入到監視器回調函數中,與由 zookeeper_init() 設置的全局監視器上下文不同,該函數設置的監視器上下文只與當前的監視器相關聯。 |
| completion | 當zoo_awget 請求完成時會調用該函數,該函數原型詳見第三講《回調函數》一節。同時傳遞給completion的rc參數為: ZOK 操作完成;ZNONODE 節點不存在;ZNOAUTH 客戶端沒有權限刪除節點。 |
| data | completion 函數被調用時,傳遞給 completion 的數據。 |
?
- 獲取子節點列表 aget_children (四個)
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watch | 如果非 0,則在服務器端設置監視,當節點發生變化時客戶端會得到通知。 |
| completion | 當 zoo_aget_children 請求完成時會調用該函數,該函數原型詳見第三講《回調函數》一節。同時傳遞給completion的rc參數為: ZOK 操作完成;ZNONODE 節點不存在;ZNOAUTH 客戶端沒有權限刪除節點。 |
| data | completion 函數被調用時,傳遞給 completion 的數據。 |
?
ZOOAPI int zoo_awget_children(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,strings_completion_t completion,const void *data);| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watcher | 如果非 0,則在服務器端設置監視,當節點發生變化時客戶端會得到通知,即使當前指定的節點不存在也會設置監視,這樣該節點被創建時,客戶端也可以得到通知。 |
| watcherCtx | 用戶指定的數據,將被傳入到監視器回調函數中,與由 zookeeper_init() 設置的全局監視器上下文不同,該函數設置的監視器上下文只與當前的監視器相關聯。 |
| completion | 當zoo_awget_children 請求完成時會調用該函數,該函數原型詳見第三講《回調函數》一節。同時傳遞給completion的rc參數為: ZOK 操作完成;ZNONODE 節點不存在;ZNOAUTH 客戶端沒有權限刪除節點。 |
| data | completion 函數被調用時,傳遞給 completion 的數據。 |
?
ZOOAPI int zoo_aget_children2(zhandle_t * zh, const char *path,int watch,strings_stat_completion_t completion,const void *data);| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watch | 如果非 0,則在服務器端設置監視,當節點發生變化時客戶端會得到通知。 |
| completion | 當 zoo_aget_children2 請求完成時會調用該函數,該函數原型詳見第三講《回調函數》一節。同時傳遞給completion的rc參數為: ZOK 操作完成;ZNONODE 節點不存在;ZNOAUTH 客戶端沒有權限刪除節點。 |
| data | completion 函數被調用時,傳遞給 completion 的數據。 |
?
ZOOAPI int zoo_awget_children2(zhandle_t * zh, const char *path,watcher_fn watcher, void *watcherCtx,strings_stat_completion_t completion,const void *data);| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| watcher | 如果非 0,則在服務器端設置監視,當節點發生變化時客戶端會得到通知,即使當前指定的節點不存在也會設置監視,這樣該節點被創建時,客戶端也可以得到通知。 |
| watcherCtx | 用戶指定的數據,將被傳入到監視器回調函數中,與由 zookeeper_init() 設置的全局監視器上下文不同,該函數設置的監視器上下文只與當前的監視器相關聯。 |
| completion | 當zoo_awget_children2 請求完成時會調用該函數,該函數原型詳見第三講《回調函數》一節。同時傳遞給completion的rc參數為: ZOK 操作完成;ZNONODE 節點不存在;ZNOAUTH 客戶端沒有權限刪除節點。 |
| data | completion 函數被調用時,傳遞給 completion 的數據。 |
?
- 訪問、設置節點 ACL 的 API
- 訪問節點 ACL
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| completion | 當 zoo_aget_acl 請求完成時會調用該函數,該函數原型詳見第三講《回調函數》一節。同時傳遞給completion的rc參數為: ZOK 操作完成;ZNONODE 節點不存在;ZNOAUTH 客戶端沒有權限刪除節點。 |
| data | completion 函數被調用時,傳遞給 completion 的數據。 |
?
- 設置節點 ACL
| zh | zookeeper_init() 返回的 zookeeper 句柄。 |
| path | 節點路徑。 |
| buffer | 保存需要設置的 ACL。 |
| buflen | buffer 的長度。 |
| completion | 當 zoo_aset_acl 請求完成時會調用該函數,該函數原型詳見第三講《回調函數》一節。同時傳遞給completion的rc參數為: ZOK 操作完成;ZNONODE 節點不存在;ZNOAUTH 客戶端沒有權限刪除節點;ZINVALIDACL 非法 ACL;ZBADVERSION 版本號不匹配。 |
| data | completion 函數被調用時,傳遞給 completion 的數據。 |
?
- 異步批處理 API
異步批處理與同步批處理方式類似見《Zookeeper C API 指南五(同步 API 介紹)》,只是需要額外設置一個 void_completion_t 回調函數,在此不再贅述。
?
以上內容是 Zookeeper? C API 中異步 API 的介紹,如有錯誤請留下您的想法和意見,我會盡快更正;同時,我也將在后面的文章中列舉一些示例來說明上述 API 的用法,如有興趣請繼續關注。
在前面的文章中我們講了大部分 Zookeeper? C API,相信大家已經知道怎樣使用 Zookeeper? C API了吧。我曾在《Zookeeper C API 指南》系列文章的第四篇《Zookeeper C API 指南四(C API 概覽)》中也 Zookeeper C API 的分為了 5 類,他們分別是:(1)、初始化、銷毀 Zookeeper 句柄,(2)、與 zoo_multi() 和 zoo_amulti() 批量操作相關的 zoo_op_t 初始化函數,(3)、同步 API,(4)、異步 API,(5)、輔助函數。其中“(1)、初始化、銷毀 Zookeeper 句柄”已經在《Zookeeper C API 指南四(C API 概覽)》中介紹過了,并且在《Zookeeper C API 指南五(同步 API 介紹)》和《Zookeeper C API 指南六(異步 API 介紹)》中我們又分別講了“同步 API” 和 “異步 API”(其中也簡單地介紹了一下與 zoo_multi() 和 zoo_amulti() 批量操作相關的 zoo_op_t 初始化函數),所以接下來我們再來講講 Zookeeper C API 中的 “輔助函數”。
我們分類的 Zookeeper C API 中輔助函數如下:
ZOOAPI void zoo_set_debug_level(ZooLogLevel logLevel);ZOOAPI void zoo_set_log_stream(FILE * logStream);ZOOAPI const clientid_t *zoo_client_id(zhandle_t * zh);ZOOAPI int zoo_recv_timeout(zhandle_t * zh);ZOOAPI const void *zoo_get_context(zhandle_t * zh);ZOOAPI void zoo_set_context(zhandle_t * zh, void *context);ZOOAPI watcher_fn zoo_set_watcher(zhandle_t * zh, watcher_fn newFn);ZOOAPI struct sockaddr *zookeeper_get_connected_host(zhandle_t * zh, struct sockaddr*addr,socklen_t * addr_len);ZOOAPI int zookeeper_interest(zhandle_t * zh, int *fd, int *interest,struct timeval *tv);ZOOAPI int zookeeper_process(zhandle_t * zh, int events);ZOOAPI int zoo_state(zhandle_t * zh);ZOOAPI const char *zerror(int c);ZOOAPI int is_unrecoverable(zhandle_t * zh);ZOOAPI void zoo_deterministic_conn_order(int yesOrNo);下面我們來講講其中比較常用的幾個函數吧 :-)
- ?設置日志等級
其中 logLevel 可以是ZOO_LOG_LEVEL_ERROR, ZOO_LOG_LEVEL_WARN,ZOO_LOG_LEVEL_INFO, ZOO_LOG_LEVEL_DEBUG四個取值。
?
- 設置日志流
Zookeeper C API 默認的日志流是標準輸出,可以通過該函數設置 Zookeeper C API的日志流為文件。
?
- 獲取客戶端的 session id,只有在客戶端的當前連接狀態有效時才可以。
?
- 返回當前會話的超時時間,只有在客戶端的當前連接狀態有效時才可以。
?
- 獲取 Zookeeper 句柄的上下文。
?
- 設置 Zookeeper 句柄的上下文。
?
- 設置 Zookeeper 句柄的全局監視器回調函數,該函數返回全局監視器的舊回調函數。
?
- 返回當前 Zookeeper 連接的套接字地址。
?
- 獲取當前 Zookeeper 連接狀態。
?
- 返回某一錯誤碼的字符串表示。
?
- 檢查當前 Zookeeper 連接是否為不可恢復的,如果不可恢復,則客戶端需要關閉連接,然后重連。
?
好了,Zookeeper 大部分的輔助函數就介紹到這里了,大家可以更多文檔可以在 zookeeper.h 中找到。:-)
前面七講我們基本上介紹完了 Zookeeper C API 的所有內容,本文將結合一個小例子講講如何在你的實際項目中使用 Zookeeper 服務。
設想如下場景:
假設程序 A 需要 7* 24 小時在線對外提供服務,但是 A 程序在生產環境下總是不穩定,時常崩潰,不過幸運的是解決方案很簡單,在 A 程序崩潰以后只需要重啟它就可以了。當然如此簡單的問題你可以提出多種解決方案,比方說自己實現一個服務程序,每隔一定時間去輪詢 A 的狀態,如果發現 A 崩潰了,立即重啟它,并向管理人員報告問題。不過我們并不打算這么做,畢竟本文主題是講 Zookeeper C API 的應用,所以我們采用 Zookeeper 服務來解決該問題。
若采用 Zookeeper 服務可以按照如下方案解決問題,程序 A 在啟動時創建一個臨時(ZOO_EPHEMERAL) znode 節點 /A,然后按照正常流程對外提供服務。另外監控程序對 /A 節點設置監視,當 /A 節點消失(說明 A 程序已經崩潰)時,重啟 A 程序。假設 A 的名稱是 QueryServer,即對外提供查詢服務的程序,具體提供什么查詢服務由應用自身決定,我們這里只是簡單地模擬一下。QueryServer 在啟動時創建一個 /QueryServer 的臨時節點(ZOO_EPHEMERAL),然后,程序 QueryServerd 監控 /QueryServer 節點,當 /QueryServer 節點消失(說明 A 程序已經崩潰)時,重啟?QueryServer 程序。
下面是 QueryServer 的實現代碼:
/** =============================================================================** Filename: QueryServer.c** Description: QueryServer** Created: 02/15/2013 08:48:49 PM** Author: Fu Haiping (forhappy), haipingf@gmail.com* Company: ICT ( Institute Of Computing Technology, CAS )** =============================================================================*/ #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <zookeeper/zookeeper.h> #include <zookeeper/zookeeper_log.h>void QueryServer_watcher_g(zhandle_t* zh, int type, int state,const char* path, void* watcherCtx) {if (type == ZOO_SESSION_EVENT) {if (state == ZOO_CONNECTED_STATE) {printf("[[[QueryServer]]] Connected to zookeeper service successfully!\n");} else if (state == ZOO_EXPIRED_SESSION_STATE) { printf("Zookeeper session expired!\n");}} }void QueryServer_string_completion(int rc, const char *name, const void *data) {fprintf(stderr, "[%s]: rc = %d\n", (char*)(data==0?"null":data), rc);if (!rc) {fprintf(stderr, "\tname = %s\n", name);} }void QueryServer_accept_query() {printf("QueryServer is running...\n"); }int main(int argc, const char *argv[]) {const char* host = "127.0.0.1:2181,127.0.0.1:2182,""127.0.0.1:2183,127.0.0.1:2184,127.0.0.1:2185";int timeout = 30000;zoo_set_debug_level(ZOO_LOG_LEVEL_WARN);zhandle_t* zkhandle = zookeeper_init(host,QueryServer_watcher_g, timeout, 0, "hello zookeeper.", 0);if (zkhandle == NULL) {fprintf(stderr, "Error when connecting to zookeeper servers...\n");exit(EXIT_FAILURE);}// struct ACL ALL_ACL[] = {{ZOO_PERM_ALL, ZOO_ANYONE_ID_UNSAFE}};// struct ACL_vector ALL_PERMS = {1, ALL_ACL};int ret = zoo_acreate(zkhandle, "/QueryServer", "alive", 5,&ZOO_OPEN_ACL_UNSAFE, ZOO_EPHEMERAL,QueryServer_string_completion, "zoo_acreate");if (ret) {fprintf(stderr, "Error %d for %s\n", ret, "acreate");exit(EXIT_FAILURE);}do {// 模擬 QueryServer 對外提供服務.// 為了簡單起見, 我們在此調用一個簡單的函數來模擬 QueryServer.// 然后休眠 5 秒,程序主動退出(即假設此時已經崩潰). QueryServer_accept_query();sleep(5);} while(false);zookeeper_close(zkhandle); }Makefile如下:
all:QueryServerQueryServer:QueryServer.ogcc -L/usr/local/lib/ -lzookeeper_mt -o $@ $^ QueryServer.o:QueryServer.cgcc -DTHREADED -I/usr/local/include/zookeeper -o $@ -c $^.PHONY:cleanclean:rm QueryServer.o QueryServer?
QueryServerd 代碼如下:
/** =============================================================================** Filename: QueryServerd.c** Description: QueryServer daemon using zookeeper. ** Created: 02/15/2013 08:48:49 PM** Author: Fu Haiping (forhappy), haipingf@gmail.com* Company: ICT ( Institute Of Computing Technology, CAS )** =============================================================================*/ #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <zookeeper/zookeeper.h> #include <zookeeper/zookeeper_log.h>void QueryServerd_watcher_global(zhandle_t * zh, int type, int state,const char *path, void *watcherCtx); static void QueryServerd_dump_stat(const struct Stat *stat); void QueryServerd_stat_completion(int rc, const struct Stat *stat,const void *data); void QueryServerd_watcher_awexists(zhandle_t *zh, int type, int state,const char *path, void *watcherCtx); static void QueryServerd_awexists(zhandle_t *zh);void QueryServerd_watcher_global(zhandle_t * zh, int type, int state,const char *path, void *watcherCtx) {if (type == ZOO_SESSION_EVENT) {if (state == ZOO_CONNECTED_STATE) {printf("Connected to zookeeper service successfully!\n");} else if (state == ZOO_EXPIRED_SESSION_STATE) { printf("Zookeeper session expired!\n");}} }static void QueryServerd_dump_stat(const struct Stat *stat) {char tctimes[40];char tmtimes[40];time_t tctime;time_t tmtime;if (!stat) {fprintf(stderr, "null\n");return;}tctime = stat->ctime / 1000;tmtime = stat->mtime / 1000;ctime_r(&tmtime, tmtimes);ctime_r(&tctime, tctimes);fprintf(stderr, "\tctime = %s\tczxid=%llx\n""\tmtime=%s\tmzxid=%llx\n""\tversion=%x\taversion=%x\n""\tephemeralOwner = %llx\n",tctimes, stat->czxid,tmtimes, stat->mzxid,(unsigned int) stat->version, (unsigned int) stat->aversion,stat->ephemeralOwner); }void QueryServerd_stat_completion(int rc, const struct Stat *stat,const void *data) {// fprintf(stderr, "%s: rc = %d Stat:\n", (char *) data, rc);// QueryServerd_dump_stat(stat); }void QueryServerd_watcher_awexists(zhandle_t *zh, int type, int state,const char *path, void *watcherCtx) {if (state == ZOO_CONNECTED_STATE) {if (type == ZOO_DELETED_EVENT) {printf("QueryServer gone away, restart now...\n");// re-exists and set watch on /QueryServer again. QueryServerd_awexists(zh);pid_t pid = fork();if (pid < 0) {fprintf(stderr, "Error when doing fork.\n");exit(EXIT_FAILURE);}if (pid == 0) { /* child process */// 重啟 QueryServer 服務.execl("/tmp/QueryServer/QueryServer", "QueryServer", NULL);exit(EXIT_SUCCESS);}sleep(1); /* sleep 1 second for purpose. */} else if (type == ZOO_CREATED_EVENT) {printf("QueryServer started...\n");}}// re-exists and set watch on /QueryServer again. QueryServerd_awexists(zh); }static void QueryServerd_awexists(zhandle_t *zh) {int ret =zoo_awexists(zh, "/QueryServer",QueryServerd_watcher_awexists,"QueryServerd_awexists.",QueryServerd_stat_completion,"zoo_awexists");if (ret) {fprintf(stderr, "Error %d for %s\n", ret, "aexists");exit(EXIT_FAILURE);} }int main(int argc, const char *argv[]) {const char *host = "127.0.0.1:2181,127.0.0.1:2182,""127.0.0.1:2183,127.0.0.1:2184,127.0.0.1:2185";int timeout = 30000;zoo_set_debug_level(ZOO_LOG_LEVEL_WARN);zhandle_t *zkhandle = zookeeper_init(host,QueryServerd_watcher_global,timeout,0, "QueryServerd", 0);if (zkhandle == NULL) {fprintf(stderr, "Error when connecting to zookeeper servers...\n");exit(EXIT_FAILURE);}QueryServerd_awexists(zkhandle);// Wait for asynchronous zookeeper call done. getchar();zookeeper_close(zkhandle);return 0; }?
Makefile 如下:
all:QueryServerdQueryServerd:QueryServerd.ogcc -L/usr/local/lib/ -lzookeeper_mt -o $@ $^ QueryServerd.o:QueryServerd.cgcc -g -DTHREADED -I/usr/local/include/zookeeper -o $@ -c $^.PHONY:cleanclean:rm QueryServerd.o QueryServerd?
首先執行 QueryServerd,
forhappy@haiping-ict:/tmp/QueryServerd$ ./QueryServerd Connected to zookeeper service successfully!然后執行 QueryServer,
forhappy@haiping-ict:/tmp/QueryServer$ ./QueryServer QueryServer is running... [[[QueryServer]]] Connected to zookeeper service successfully! [zoo_acreate]: rc = 0name = /QueryServer可見 Queryerver 創建了 /QueryServer 節點,5 秒后 QueryServer 模擬程序崩潰而退出,那么此時在 QueryServerd 端輸出如下:
Connected to zookeeper service successfully! QueryServer started... # QueryServerd 感知到 QueryServer 已正常啟動. QueryServer gone away, restart now... # 5 秒鐘后,QueryServer 崩潰,QueryServerd 準備重啟 QueryServer. QueryServer is running... #?QueryServer 正在運行,以下 3 行是 QueryServer 輸出結果。 [[[QueryServer]]] Connected to zookeeper service successfully! [zoo_acreate]: rc = 0name = /QueryServer QueryServer started... #?QueryServerd 感知到 QueryServer 已正常啟動. QueryServer gone away, restart now...# 又過了 5 秒鐘后,QueryServer 崩潰,QueryServerd 準備重啟 QueryServer. QueryServer is running... #?QueryServer 再次運行,以下 3 行是 QueryServer 輸出結果。 [[[QueryServer]]] Connected to zookeeper service successfully! [zoo_acreate]: rc = 0name = /QueryServer QueryServer started... #?QueryServerd 再次感知到 QueryServer 已正常啟動,如此反復. QueryServer gone away, restart now... QueryServer is running... [[[QueryServer]]] Connected to zookeeper service successfully! [zoo_acreate]: rc = 0name = /QueryServer QueryServer started...即 QueryServer 每 5 秒鐘崩潰一次,然后又被 QueryServerd 重啟,模擬了上面的應用場景。
好了 Zookeeper C API 的應用小示例講完了,可能應用場景選取的不好,不過大致可以一些說明問題吧,如果你想看 Zookeeper 更貼近現實的應用場景,可以參考淘寶的一篇文章《ZooKeeper典型應用場景一覽》和 IBM developerWorks 的一篇博文《分布式服務框架 Zookeeper -- 管理分布式環境中的數據》。
總結
以上是生活随笔為你收集整理的Zookeeper C API 指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: libuv 中文编程指南
- 下一篇: 统计一下你写过多少代码