使用zookeeper实现分布式master选举(c 接口版本)

时间:2021-11-04 16:39:59

http://www.cnblogs.com/zhejiangxiaomai/p/5729638.html

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 否则一点反应都没)

使用zookeeper实现分布式master选举(c 接口版本)
  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 }