zookeeper,已經被不少人所熟知,主要應用場景有(數據訂閱/發佈 ,負載均衡, 命名服務, 分佈式協調/通知,集羣管理,Master選舉,分佈式鎖,分佈式隊列)。ios
C接口的描述 主要參考 Haippy 的文章 :Zookeeper C API 指南 (感謝大神)c++
可是網上的C++版 示例代碼少之又少,做爲一個小白,本身摸索,給你們參考。負載均衡
Master選舉的需求主要以下:分佈式
1.多臺機器同時進行選舉,產生惟一的一臺機器做爲Master,其它的機器做爲slave。函數
2.當Master宕機後,須要在全部存活的slave中再次選舉一個成爲新的Master。spa
3.當新的master宕機後,繼續選舉,如此反覆。debug
這樣保證了在大多數分佈式系統中Master不會宕機。在這裏咱們默認zookeeper是高可用的。(即不考慮zookeeper失效。)c++11
思路主要以下:code
1.每一個進程都會去建立一個臨時文件(名爲master,該文件裏面的值爲master的ip地址),zookeeper將會保證那麼多的進程只有一個進程可以建立成功。收到ZOK,其它的都會收到ZNODEEXISTS(已存在)。server
2.沒有建立成功的節點slave節點將會去監視這個已經建立成功的「文件」(master) ,當該文件發生變化時,slave將會觸發相對應的函數。
臨時文件:爲ZOO_EPHEMERAL類型,正如 EPHEMERAL(短暫的)本意,當建立這個文件的進程掛了,zookeeper會自動檢測到(正是利用這個特性)。
變化: 這個變化有不少種可能,如該文件的內容變了,該文件下的子文件增長或減小了,(在zookeeper中文件既是文件又是一個文件夾),也多是該文件被刪除了。
值的一提的是,如Haippy所述,zookeeper watch函數是 「一次性」的,簡單的說 小明 調用了watch觀察 本身家的門。 此時,小紅打開了門,watch函數馬上告訴了小明,小明就過去把門關了。這個時候若是小明沒有再次調用watch函數,小紅再偷偷的開門,小明是不知道的,講了那麼囉嗦其實就是一句話「每次觸發watch函數後,請別忘記再次調用watch函數」(固然仍是要看具體的場景)。
下面貼上代碼(全部的解釋都寫在代碼註釋中): 編譯命令爲 g++ election.cpp -o election -lzookeeper_mt -std=c++11 (不是-lzookeeper_st 不然一點反應都沒)
1 /* 2 * ============================================================================= 3 * 4 * Filename: election.cpp 5 * 6 * Description: elect master test by zookeeper 7 * 8 * Created: 02/15/2013 08:48:49 PM 9 * 10 * Author: zhejiangxiaomai, 358088534@qq.com 11 * Company: ECNU 12 * 13 * ============================================================================= 14 */ 15 #include <unistd.h> 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <zookeeper/zookeeper.h> 20 #include <zookeeper/zookeeper_log.h> 21 #include <iostream> 22 using namespace std; 23 24 int status = -1; 25 // 1 : master 0 :slave 26 zhandle_t* init(const char* host, int timeout); 27 void init_1(zhandle_t* zh, int type, int state, const char* path, void* watcherCtx); 28 void getResult(int rc, const char *name, const void *data); 29 void go_to_election(zhandle_t* zh, int type, int state, const char* path, void* ip); 30 void get_master(int rc, const char *value, int value_len, const struct Stat *stat, const void *data); 31 void watch_master(zhandle_t* zh, const char* ip); 32 33 int main(int argc, const char *argv[]) 34 { 35 const char* host = "127.0.0.1:2181"; 36 int timeout = 30000; 37 zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); 38 const char* ip = argv[1]; 39 //初始化鏈接句柄 40 zhandle_t* zh = init(host, timeout); 41 //參加選舉 42 go_to_election(zh,0,0,"/master",(void *) ip); 43 //按下空格便可清除句柄,退出程序 44 getchar(); 45 zookeeper_close(zh); 46 return 0; 47 } 48 //建立鏈接zookeeper的句柄 49 zhandle_t* init(const char* host, int timeout){ 50 zhandle_t* zkhandle = zookeeper_init(host,init_1,timeout, 0, NULL, 0); 51 if (zkhandle == NULL) { 52 fprintf(stderr, "Error when connecting to zookeeper servers...\n"); 53 exit(EXIT_FAILURE); 54 } 55 return zkhandle; 56 } 57 //作爲建立句柄的watch函數(通常輸出句柄的信息,可是我沒有處理) 58 void init_1(zhandle_t* zh, int type, int state, const char* path, void* watcherCtx) 59 { 60 std::cout<<"init handler"<<endl; 61 } 62 //參選函數 63 void go_to_election(zhandle_t* zh, int type, int state, const char* path, void* ip) 64 { 65 //建立一個/master文件,值爲char* 類型的IP,長度爲15, 66 //該文件爲類型爲ZOO_EPHEMERAL,當建立完成後會調用getResult函數 67 int ret = zoo_acreate(zh, "/master", (char*)ip, 15, 68 &ZOO_OPEN_ACL_UNSAFE, ZOO_EPHEMERAL, 69 getResult, "election"); 70 if (ret) { 71 fprintf(stderr, "Error %d for %s\n", ret, "election"); 72 exit(EXIT_FAILURE); 73 } 74 //此處sleep一秒,沒有這個步驟程序就沒有效果, 75 //我認爲是create沒有那麼快,沒有建立完成的watch就沒有意義。 76 sleep(1); 77 if(status == 0) 78 { 79 //若是是slave就觀察master 80 watch_master(zh, (const char*)ip); 81 } 82 } 83 //判斷建立結果,結果保存在rc中,並改變狀態。 84 //在此看起來其餘參數沒有用到,可是這是七種回調函數的一種格式,須要遵照。 85 void getResult(int rc, const char *name, const void *data) 86 { 87 switch(rc){ 88 case ZOK: 89 std::cout<<"Become master"<<endl; 90 status = 1; 91 break; 92 case ZNODEEXISTS: 93 std::cout<<"Become slave, keep watch"<<endl; 94 status = 0; 95 break; 96 } 97 } 98 //在此僅僅是將master文件的值輸出. 99 void get_master(int rc, const char *value, int value_len, 100 const struct Stat *stat, const void *data){ 101 std::cout<<(char *)data<<(char*)value<<endl; 102 } 103 104 void watch_master(zhandle_t* zh, const char* ip){ 105 //獲取/master文件的值 經過回調函數 get_master. 當該文件發生改變時帶着本身的ip地址從新去註冊 106 int ret = zoo_awget(zh, "/master",go_to_election,(void *)ip , 107 get_master,"get master ip :"); 108 if (ret) { 109 fprintf(stderr, "Error %d for %s\n", ret, "re"); 110 exit(EXIT_FAILURE); 111 } 112 }
實現結果圖:(master從0.1 變爲了0.2 變爲了0.3)