使用zookeeper實現分布式master選舉(c 接口版本)


zookeeper,已經被很多人所熟知,主要應用場景有(數據訂閱/發布 ,負載均衡, 命名服務, 分布式協調/通知,集群管理,Master選舉,分布式鎖,分布式隊列)。

C接口的描述  主要參考 Haippy 的文章 :Zookeeper C API 指南 (感謝大神)

但是網上的C++版 示例代碼少之又少,作為一個小白,自己摸索,給大家參考。

Master選舉的需求主要如下:

         1.多台機器同時進行選舉,產生唯一的一台機器作為Master,其它的機器作為slave。

         2.當Master宕機后,需要在所有存活的slave中再次選舉一個成為新的Master。

         3.當新的master宕機后,繼續選舉,如此反復。

這樣保證了在大多數分布式系統中Master不會宕機。在這里我們默認zookeeper是高可用的。(即不考慮zookeeper失效。)

思路主要如下:

        1.每個進程都會去創建一個臨時文件(名為master,該文件里面的值為master的ip地址),zookeeper將會保證那么多的進程只有一個進程能夠創建成功。收到ZOK,其它的都會收到ZNODEEXISTS(已存在)。

        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)

 


免責聲明!

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



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