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


    1. 集群机器监控

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

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

    1. client在节点 x 上注冊一个Watcher。那么假设x的子节点变化了,会通知该client
    2. 创建EPHEMERAL类型的节点。一旦client和server的会话结束或过期,那么该节点就会消失
    

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

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

    2. Master选举

    在分布式环境中,有些业务逻辑仅仅须要集群中的某一台机器进行运行,其它的机器能够共享这个结果。这样能够大大降低反复计算,提高性能,于是就须要进行master选举。

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

    此外。也能够利用Zookeeper的EPHEMERAL_SEQUENTIAL节点,实现动态选举:每一个client都在/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!
    ",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!
    ",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!
    ",path);
        }else{
            char child_path[512] ={0};
            char ip_pid[64] = {0};
            int ip_pid_len = sizeof(ip_pid);
            printf("--------------
    ");
            printf("ip	pid
    ");
            for(i = 0; i < procs.count; ++i){
                sprintf(child_path,"%s/%s",path,procs.data[i]);
                //printf("%s
    ",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!
    ",child_path);
                }else if(strcmp(ip_pid,localhost)==0){
                    printf("%s(Master)
    ",ip_pid);
                }else{
                    printf("%s
    ",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
    ");  
        printf("type: %d
    ", type);  
        printf("state: %d
    ", state);  
        printf("path: %s
    ", path);  
        printf("watcherCtx: %s
    ", (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] 
    ");
        printf("        -h Show help
    ");
        printf("        -m set monitor mode
    ");
        printf("        -s zookeeper server ip:port
    ");
        printf("For example:
    ");
        printf("monitor -m -s172.17.0.36:2181 
    ");
    }
    
    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 ", optopt); exit(-1); case ':': print_usage(); printf("need parameter: %c ", 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 "); printf("type: %d ", type); printf("state: %d ", state); printf("path: %s ", path); printf("watcherCtx: %s ", (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! ",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! ",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! ",path); }else{ char child_path[512] ={0}; char ip_pid[64] = {0}; int ip_pid_len = sizeof(ip_pid); printf("-------------- "); printf("ip pid "); for(i = 0; i < procs.count; ++i){ sprintf(child_path,"%s/%s",path,procs.data[i]); //printf("%s ",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! ",child_path); }else if(strcmp(ip_pid,localhost)==0){ printf("%s(Master) ",ip_pid); }else{ printf("%s ",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... "); 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! ",path); }else{ printf("create path %s successfully! ",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! ",child_path,path_buffer); }else{ printf("create child path %s successfully! ",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的server的ip和port。(注意不要理解成master的ip和port哦)
    參数-m表示该进程是一个独立的监控进程。注意,指定这个參数的进程是不參加选主的,由于它不会在/Monitor文件夹下创建路径。


    执行演示样例:
    monitor -s172.17.0.36:2181


  • 相关阅读:
    后端——框架——日志框架——logback——《官网》阅读笔记——第三章节(配置文件)
    后端——框架——日志框架——logback——《官网》阅读笔记——第四章节(Appender)——待完善
    后端——框架——日志框架——logback——《官网》阅读笔记——第五章节(Encoder)
    后端——框架——日志框架——logback——《官网》阅读笔记——第六章节(Layout和日志格式)
    后端——框架——日志框架——logback——《官网》阅读笔记——第七章节(Filter)
    后端——框架——日志框架——logback——《官网》阅读笔记——第八章节(多线程场景&MDC对象)
    后端——框架——日志框架——logback——《官网》阅读笔记——第九章节(多应用)——待完善
    后端——框架——日志框架——logback——《官网》阅读笔记——第十章节(JMX)
    后端——框架——日志框架——logback——《官网》阅读笔记——第十一章节——待补充
    后端——框架——日志框架——logback——《官网》阅读笔记——第十二章节——待补充
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/5342165.html
Copyright © 2020-2023  润新知