ZooKeeper场景实践:(6)集群监控和Master选举

1. 集群机器监控

这通常用于那种对集群中机器状态,机器在线率有较高要求的场景,能够快速对集群中机器变化作出响应。这样的场景中,往往有一个监控系统,实时检测集群机器是否存活。

利用ZooKeeper有两个特性(读可监控,临时节点),就可以实现一种集群机器存活性监控系统:

1. 客户端在节点 x 上注册一个Watcher,那么如果x的子节点变化了,会通知该客户端
2. 创建EPHEMERAL类型的节点,一旦客户端和服务器的会话结束或过期,那么该节点就会消失

利用这两个特性,可以分别实现对客服端的状态变化、上下线进行监控。

例如,监控系统在 /Monitor 节点上注册一个Watcher,以后每动态加机器,那么就往 /Monitor 下创建一个 EPHEMERAL类型的节点:/Monitor/{hostname}. 这样,监控系统就能够实时知道机器的增减情况,至于后续处理就是监控系统的业务了。

2. Master选举

在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机器可以共享这个结果,这样可以大大减少重复计算,提高性能,于是就需要进行master选举。

利用ZooKeeper的强一致性,能够保证在分布式高并发情况下节点创建的全局唯一性,即:同时有多个客户端请求创建 /currentMaster 节点,最终一定只有一个客户端请求能够创建成功。利用这个特性,就能很轻易的在分布式环境中进行集群选举了。

此外,也可以利用Zookeeper的EPHEMERAL_SEQUENTIAL节点,实现动态选举:每个客户端都在/Master/下创建一个EPHEMERAL_SEQUENTIAL节点,由于ZooKeeper保证SEQUENTIAL的有序性,因此我们可以简单的把节点号最小的作为Master,就完成了选主。

3. 场景分析

假设我们要监控集群中的一群活动的业务进程,同时会在这群进程中选取一个进程作为监控的Master进程。每个进程使用IP地址加进程号标识,即{ip:pid}.当新的业务进程上线时,该进程会到/Monitor下创建一个临时有序(EPHEMERAL_SEQUENTIAL)的节点.并获取/Monitor下的子节点列表,如果发现自己创建的节点最小,则提升自己为Master进程,否则仍是业务进程。当进程退出时该节点会自动删除,其他进程则会尝试选主,保证当Master进程退出后,会提升一个新的Master进程。

举个例子,假设集群中一开始没有进程,

  1. 进程A1被创建,在/Monitor创建/Monitor/proc-1路径,由于/Monitor下只有一个路径,A1被提升为Master进程。
  2. 进程A2被创建,在/Monitor创建/Monitor/proc-2路径,选主不成功,作为Slave进程;同时A1监控/Monitor的子节点变化事件,会收到有新进程被创建 ,因此执行show_list。
  3. 进程A2被创建,在/Monitor创建/Monitor/proc-3路径,选主不成功,作为Slave进程;同时A1监控/Monitor的子节点变化事件,会收到有新进程被创建 ,因此执行show_list。
  4. 进程A1被Killed掉,其他进程监控到/Monitor的子节点变化事件,尝试选主,只有A2序号成功,因此A2选主成功,A3作为Slave进程。
  5. 进程A4被创建,在/Monitor创建/Monitor/proc-4路径,选主不成功,作为Slave进程;同时A2监控/Monitor的子节点变化事件,会收到有新进程被创建 ,因此执行show_list。

执行情况如下表所示:

A1 A2 A3 A4
create,show_list(M)      
show_list(M) create    
show_list(M) - create  
killed show_list(M) -  
- show_list(M) - create

4. 动手实践

首先是获取本机的IP已经当前进程的进程号PID,并通过ip_pid返回。

void getlocalhost(char *ip_pid,int len)
{
    char hostname[64] = {0};
    struct hostent *hent ;

    gethostname(hostname,sizeof(hostname));
    hent = gethostbyname(hostname);

    char * localhost = inet_ntoa(*((struct in_addr*)(hent->h_addr_list[0])));

    snprintf(ip_pid,len,"%s:%lld",localhost,getpid());
}

选主函数,获取path下的所有子节点,选择序号最小的一个,取出它的ip_pid,如果和本进程相同,则本进程被选为Master。如果当前进程被选为Master,则进程中的全局变量g_mode会被赋值为MODE_MONITOR,否则不变。

void choose_mater(zhandle_t *zkhandle,const char *path)
{
    struct String_vector procs;
    int i = 0;
    int ret = zoo_get_children(zkhandle,path,1,&procs);

    if(ret != ZOK || procs.count == 0){
        fprintf(stderr,"failed to get the children of path %s!\n",path);
    }else{
        char master_path[512] ={0};
        char ip_pid[64] = {0};
        int ip_pid_len = sizeof(ip_pid);

        char master[512]={0};
        char localhost[512]={0};

        getlocalhost(localhost,sizeof(localhost));

        strcpy(master,procs.data[0]);
        for(i = 1; i < procs.count; ++i){
            if(strcmp(master,procs.data[i])>0){
                strcpy(master,procs.data[i]);
            }
        }

        sprintf(master_path,"%s/%s",path,master);

        ret = zoo_get(zkhandle,master_path,0,ip_pid,&ip_pid_len,NULL);
        if(ret != ZOK){
            fprintf(stderr,"failed to get the data of path %s!\n",master_path);
        }else if(strcmp(ip_pid,localhost)==0){
            g_mode = MODE_MONITOR;
        }

    }

    for(i = 0; i < procs.count; ++i){
        free(procs.data[i]);
        procs.data[i] = NULL;
    }

}

show_list为Master进程函数,所做的任务为打印path目录下所有子节点的ip_pid.

void show_list(zhandle_t *zkhandle,const char *path)
{

    struct String_vector procs;
    int i = 0;
    char localhost[512]={0};

    getlocalhost(localhost,sizeof(localhost));

    int ret = zoo_get_children(zkhandle,path,1,&procs);

    if(ret != ZOK){
        fprintf(stderr,"failed to get the children of path %s!\n",path);
    }else{
        char child_path[512] ={0};
        char ip_pid[64] = {0};
        int ip_pid_len = sizeof(ip_pid);
        printf("--------------\n");
        printf("ip\tpid\n");
        for(i = 0; i < procs.count; ++i){
            sprintf(child_path,"%s/%s",path,procs.data[i]);
            //printf("%s\n",child_path);
            ret = zoo_get(zkhandle,child_path,0,ip_pid,&ip_pid_len,NULL);
            if(ret != ZOK){
                fprintf(stderr,"failed to get the data of path %s!\n",child_path);
            }else if(strcmp(ip_pid,localhost)==0){
                printf("%s(Master)\n",ip_pid);
            }else{
                printf("%s\n",ip_pid);
            }
        }
    }

    for(i = 0; i < procs.count; ++i){
        free(procs.data[i]);
        procs.data[i] = NULL;
    }
}

监控函数如下,当发现path的子节点发生变化,就会尝试重新选主,如果当前进程被选为主,就立即执行show_list,打印path下的所有子节点对应的ip_pid.

void zktest_watcher_g(zhandle_t* zh, int type, int state, const char* path, void* watcherCtx)
{
/*
    printf("watcher event\n");
    printf("type: %d\n", type);
    printf("state: %d\n", state);
    printf("path: %s\n", path);
    printf("watcherCtx: %s\n", (char *)watcherCtx);
*/  

    if(type == ZOO_CHILD_EVENT &&
       state == ZOO_CONNECTED_STATE ){

        choose_mater(zh,path);
        if(g_mode == MODE_MONITOR){
            show_list(zh,path);
        }
    }
}

完整代码如下:
1.monitor.c

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include"zookeeper.h"
#include"zookeeper_log.h"  

enum WORK_MODE{MODE_MONITOR,MODE_WORKER} g_mode;
char g_host[512]= "172.17.0.36:2181";  

//watch function when child list changed
void zktest_watcher_g(zhandle_t* zh, int type, int state, const char* path, void* watcherCtx);
//show all process ip:pid
void show_list(zhandle_t *zkhandle,const char *path);
//if success,the g_mode will become MODE_MONITOR
void choose_mater(zhandle_t *zkhandle,const char *path);
//get localhost ip:pid
void getlocalhost(char *ip_pid,int len);

void print_usage();
void get_option(int argc,const char* argv[]);

/**********unitl*********************/
void print_usage()
{
    printf("Usage : [monitor] [-h] [-m] [-s ip:port] \n");
    printf("        -h Show help\n");
    printf("        -m set monitor mode\n");
    printf("        -s zookeeper server ip:port\n");
    printf("For example:\n");
    printf("monitor -m -s172.17.0.36:2181 \n");
}

void get_option(int argc,const char* argv[])
{
    extern char    *optarg;
    int            optch;
    int            dem = 1;
    const char    optstring[] = "hms:";

    //default
    g_mode = MODE_WORKER;

    while((optch = getopt(argc , (char * const *)argv , optstring)) != -1 )
    {
        switch( optch )
        {
        case 'h':
            print_usage();
            exit(-1);
        case '?':
            print_usage();
            printf("unknown parameter: %c\n", optopt);
            exit(-1);
        case ':':
            print_usage();
            printf("need parameter: %c\n", optopt);
            exit(-1);
        case 'm':
                g_mode = MODE_MONITOR;
            break;
        case 's':
            strncpy(g_host,optarg,sizeof(g_host));
            break;
        default:
            break;
        }
    }
}
void zktest_watcher_g(zhandle_t* zh, int type, int state, const char* path, void* watcherCtx)
{
/*
    printf("watcher event\n");
    printf("type: %d\n", type);
    printf("state: %d\n", state);
    printf("path: %s\n", path);
    printf("watcherCtx: %s\n", (char *)watcherCtx);
*/  

    if(type == ZOO_CHILD_EVENT &&
       state == ZOO_CONNECTED_STATE ){

        choose_mater(zh,path);
        if(g_mode == MODE_MONITOR){
            show_list(zh,path);
        }
    }
}
void getlocalhost(char *ip_pid,int len)
{
    char hostname[64] = {0};
    struct hostent *hent ;

    gethostname(hostname,sizeof(hostname));
    hent = gethostbyname(hostname);

    char * localhost = inet_ntoa(*((struct in_addr*)(hent->h_addr_list[0])));

    snprintf(ip_pid,len,"%s:%lld",localhost,getpid());
}

void choose_mater(zhandle_t *zkhandle,const char *path)
{
    struct String_vector procs;
    int i = 0;
    int ret = zoo_get_children(zkhandle,path,1,&procs);

    if(ret != ZOK || procs.count == 0){
        fprintf(stderr,"failed to get the children of path %s!\n",path);
    }else{
        char master_path[512] ={0};
        char ip_pid[64] = {0};
        int ip_pid_len = sizeof(ip_pid);

        char master[512]={0};
        char localhost[512]={0};

        getlocalhost(localhost,sizeof(localhost));

        strcpy(master,procs.data[0]);
        for(i = 1; i < procs.count; ++i){
            if(strcmp(master,procs.data[i])>0){
                strcpy(master,procs.data[i]);
            }
        }

        sprintf(master_path,"%s/%s",path,master);

        ret = zoo_get(zkhandle,master_path,0,ip_pid,&ip_pid_len,NULL);
        if(ret != ZOK){
            fprintf(stderr,"failed to get the data of path %s!\n",master_path);
        }else if(strcmp(ip_pid,localhost)==0){
            g_mode = MODE_MONITOR;
        }

    }

    for(i = 0; i < procs.count; ++i){
        free(procs.data[i]);
        procs.data[i] = NULL;
    }

}
void show_list(zhandle_t *zkhandle,const char *path)
{

    struct String_vector procs;
    int i = 0;
    char localhost[512]={0};

    getlocalhost(localhost,sizeof(localhost));

    int ret = zoo_get_children(zkhandle,path,1,&procs);

    if(ret != ZOK){
        fprintf(stderr,"failed to get the children of path %s!\n",path);
    }else{
        char child_path[512] ={0};
        char ip_pid[64] = {0};
        int ip_pid_len = sizeof(ip_pid);
        printf("--------------\n");
        printf("ip\tpid\n");
        for(i = 0; i < procs.count; ++i){
            sprintf(child_path,"%s/%s",path,procs.data[i]);
            //printf("%s\n",child_path);
            ret = zoo_get(zkhandle,child_path,0,ip_pid,&ip_pid_len,NULL);
            if(ret != ZOK){
                fprintf(stderr,"failed to get the data of path %s!\n",child_path);
            }else if(strcmp(ip_pid,localhost)==0){
                printf("%s(Master)\n",ip_pid);
            }else{
                printf("%s\n",ip_pid);
            }
        }
    }

    for(i = 0; i < procs.count; ++i){
        free(procs.data[i]);
        procs.data[i] = NULL;
    }
}

int main(int argc, const char *argv[])
{
    int timeout = 30000;
    char path_buffer[512];
    int bufferlen=sizeof(path_buffer);  

    zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); //设置日志级别,避免出现一些其他信息  

    get_option(argc,argv);

    zhandle_t* zkhandle = zookeeper_init(g_host,zktest_watcher_g, timeout, 0, (char *)"Monitor Test", 0);  

    if (zkhandle ==NULL)
    {
        fprintf(stderr, "Error when connecting to zookeeper servers...\n");
        exit(EXIT_FAILURE);
    }  

    char path[512]="/Monitor";

    int ret = zoo_exists(zkhandle,path,0,NULL);
    if(ret != ZOK){
        ret = zoo_create(zkhandle,path,"1.0",strlen("1.0"),
                          &ZOO_OPEN_ACL_UNSAFE,0,
                          path_buffer,bufferlen);
        if(ret != ZOK){
            fprintf(stderr,"failed to create the path %s!\n",path);
        }else{
            printf("create path %s successfully!\n",path);
        }
    }

    if(ret == ZOK && g_mode == MODE_WORKER){

        char localhost[512]={0};
        getlocalhost(localhost,sizeof(localhost));

        char child_path[512];
        sprintf(child_path,"%s/proc-",path);
        ret = zoo_create(zkhandle,child_path,localhost,strlen(localhost),
                          &ZOO_OPEN_ACL_UNSAFE,ZOO_SEQUENCE|ZOO_EPHEMERAL,
                          path_buffer,bufferlen);
        if(ret != ZOK){
            fprintf(stderr,"failed to create the child_path %s,buffer:%s!\n",child_path,path_buffer);
        }else{
            printf("create child path %s successfully!\n",path_buffer);
        }
        choose_mater(zkhandle,path);

    }

    if(g_mode == MODE_MONITOR){
        show_list(zkhandle,path);
    }

    getchar();

    zookeeper_close(zkhandle); 

    return 0;
}

2.Makefile

CC=gcc
CFLAGS=-g
ZOOKEEPER_INSTALL=/usr/local
ZOOKEEPER_INC=-I${ZOOKEEPER_INSTALL}/include/zookeeper
ZOOKEEPER_LIB= -L${ZOOKEEPER_INSTALL}/lib -lzookeeper_mt

APP=monitor
all:
    ${CC} monitor.c -DTHREAD ${CFLAGS} ${ZOOKEEPER_INC} ${ZOOKEEPER_LIB} -o ${APP}
clean:
    rm -f ${APP}

可以单机上重复启动程序,它们的进程号都是不同的,也可以在集群中启动程序。
参数-s表示Zookeeper的服务器的ip和端口,(注意不要理解成master的ip和端口哦)
参数-m表示该进程是一个独立的监控进程,注意,指定这个参数的进程是不参加选主的,因为它不会在/Monitor目录下创建路径。
运行示例:
monitor -s172.17.0.36:2181

时间: 2024-10-23 20:40:20

ZooKeeper场景实践:(6)集群监控和Master选举的相关文章

ZooKeeper场景实践:(1)准备工作

ZooKeeper是一个高可用的分布式数据管理与系统协调框架.保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得ZooKeeper解决很多分布式问题. 有人认为ZooKeeper之于分布式的意义正如同lex/yacc之于编译的意义.我们知道lex/yacc是一套强大的语法编译工具.使用lex/yacc可以很轻松的完成许多语法规则的编写.同样道理,Zookeeper作为一个分布式的数据管理和协调框架,没有它依然可以做分布式,但是有了它,你的分布式会更轻松. 本系列文章主要是从学习的角度对

【2】基于zookeeper,quartz,rocketMQ实现集群化定时系统

<一>项目结构图     (1)ZK协调分配 ===>集群中的每一个定时服务器与zookeeper交互,由集群中的master节点进行任务划分,并将划分结果分配给集群中的各个服务器节点. ===>保证每台定时服务器的节点持有唯一的定时任务. ===>当集群中有节点宕机,保证宕机的节点持有的任务会被重新分配到正常运行的服务器节点上. ===>将协调的结果交给本地容器   (2)本地容器 ===>持有本定时服务器持有的定时任务 ===>将本地容器的任务推送到qu

Mesos+Zookeeper+Marathon+Docker分布式集群管理最佳实践

目录  Mesos简介 Zookeeper简介 Marathon简介 docker集群实践 Mesos集群部署   一.Mesos简介   Mesos是Apache下的开源分布式资源管理框架,它被称为分布式系统的内核.Mesos最初是由加州大学伯克利分校的AMPLab开发,后在Twitter得到广泛使用.   Mesos-Master:主要负责管理各个framework和slave,并将slave上的资源分配给各个framework. Mesos-Slave:负责管理本节点上的各个mesos-t

高性能的Linux集群监控之道

  监控是集群管理的核心任务.监控数据可用于调度任务.负载平衡.向管理员报告软硬件故障,并广泛地控制系统使用情况.监控信息必须在不影响集群性能的情况下获得.本文将讨论使用/proc文件系统和Java来获得监控数据的方法. Java在Linux集群中的应用 Java技术为集群管理开发者提供了许多解决问题的办法.Java是动态.灵活.可移植的,这些不寻常的特征使得它成为了在异构网络及平台上构造集群管理的理想基础. Java具有广泛的例程库,很容易处理IP协议,如TCP.UDP,并可在multi-ho

集群监控系统Ganglia应用案例

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://chenguang.blog.51cto.com/350944/1330114 集群监控系统Ganglia应用案例 --我们把集群系统投入生产环境后,这时就需要一套可视化的工具来监视集群系统,这将有助于我们迅速地了解机群的整体配置情况,准确地把握机群各个监控节点的信息,全面地察看监控节点的性能指标,使机群系统具有较高的管理性.监视系统的主要目标是从各个监控节点采集监控信息,如CP

Kubernetes集群监控指南

本文讲的是Kubernetes集群监控指南[编者的话]本文讨论了 Kubernetes 对运维监控的改变,以及我们应该如何合理得监控一个由 Kubernetes 编排的容器化基础设施. [3 天烧脑式容器存储网络训练营 | 深圳站]本次培训以容器存储和网络为主题,包括:Docker Plugin.Docker storage driver.Docker Volume Pulgin.Kubernetes Storage机制.容器网络实现原理和模型.Docker网络实现.网络插件.Calico.Co

HBase 集群监控

为什么需要监控? 为了保证系统的稳定性,可靠性,可运维性. 掌控集群的核心性能指标,了解集群的性能表现. 集群出现问题时及时报警,便于运维同学及时修复问题. 集群重要指标值异常时进行预警,将问题扼杀在摇篮中,不用等集群真正不可用时才采取行动. 当集群出现问题时,监控系统可以帮助我们更快的定位问题和解决问题 如何构建 HBase 集群监控系统? 公司有自己的监控系统,我们所要做的就是将 HBase 中我们关心的指标项发送到监控系统去,问题就转换为我们开发,采集并返回哪些 HBase 集群监控指标项

Hadoop YARN学习之监控集群监控Nagios(4)

doop YARN学习之监控集群监控Nagios(4) 1. Nagios是一个流行的开源监控工具,可以用来监控Hadoop集群. 2. 监控基本的Hadoop服务 调试好脚本后命名为chek_resource_manager.sh,并把它放在Nagios的插件目录中. 加载Nagios插件向hadoop-cluster.cfg添加如下信息 define command{ command_name check_resource_manager command_line /usr/lib64/na

hbase 集群启动后master 端口监听不正确

问题描述 hbase 集群启动后master 端口监听不正确 截图是在master机器上端口监听,可以看到60000.60020是监听在127.0.0.1上的 这样就导致其他的slave 机器无法访问60000.60020端口,网上说是hosts配置不正确,但是都各种修改了还是不正确,请问该如何解决