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!\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的server的ip和port。(注意不要理解成master的ip和port哦)

參数-m表示该进程是一个独立的监控进程。注意,指定这个參数的进程是不參加选主的,由于它不会在/Monitor文件夹下创建路径。

执行演示样例:

monitor -s172.17.0.36:2181

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

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

    <一>项目结构图 (1)ZK协调分配 ===>集群中的每一个定时服务器与zookeeper交互,由集群中的master节点进行任务划分,并将划分结果分配给集群中的各个服务器节点. = ...

  2. vivo 容器集群监控系统架构与实践

    vivo 互联网服务器团队-YuanPeng 一.概述 从容器技术的推广以及 Kubernetes成为容器调度管理领域的事实标准开始,云原生的理念和技术架构体系逐渐在生产环境中得到了越来越广泛的应用实 ...

  3. 分布式协调服务Zookeeper集群监控JMX和ZkWeb应用对比

    分布式协调服务Zookeeper集群监控JMX和ZkWeb应用对比 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. JMX是用来远程监控Java应用的框架,这个也可以用来监控其他的J ...

  4. Zookeeper Monitor集群监控开发

    随着线上越来越多的系统依赖Zookeeper集群.以至于Zookeeper集群的执行状况越来越重要.可是眼下还没有什么好用的Zookeeper集群监控系统(淘宝开源了一个Zookeeper监控系统,可 ...

  5. Zookeeper实现分布式集群监控

    Zookeeepr实现分布式集群监控 Zookeeper中节点有两种:临时节点和永久节点 从类型上看节点又可以分为四种节点类型:PERSIST,PERSIST_SEQUENTIAL,EPHEMERAL ...

  6. kafka集群监控之kafka-manager部署(kafka-manager的进程为:ProdServerStart)

    kafka集群监控之kafka-manager部署(ProdServerStart) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 雅虎官网GitHub项目:https://git ...

  7. zookeeper集群搭建及Leader选举算法源码解析

    第一章.zookeeper概述 一.zookeeper 简介 zookeeper 是一个开源的分布式应用程序协调服务器,是 Hadoop 的重要组件. zooKeeper 是一个分布式的,开放源码的分 ...

  8. SpringCloud (十) Hystrix Dashboard单体监控、集群监控、与消息代理结合

    一.前言 Dashboard又称为仪表盘,是用来监控项目的执行情况的,本文旨在Dashboard的使用 分别为单体监控.集群监控.与消息代理结合. 代码请戳我的github 二.快速入门 新建一个Sp ...

  9. HBase 集群监控系统构建

    HBase 集群监控系统构建 标签(空格分隔): Hbase 一, 集群为什么需要监控系统 总的来说是为了保证系统的稳定性,可靠性,可运维性.具体来说我认为有以下几点: 掌控集群的核心性能指标,了解集 ...

随机推荐

  1. 网络基本功(八):细说TCP滑动窗口

    https://community.emc.com/message/842129#842129

  2. SQLite入门与分析(七)---浅谈SQLite的虚拟机

    写在前面:虚拟机技术在现在是一个非常热的技术,它的历史也很悠久.最早的虚拟机可追溯到IBM的VM/370,到上个世纪90年代,在计算机程序设计语言领域又出现一件革命性的事情——Java语言的出现,它与 ...

  3. logstash gsub替换

    { "message" => "192.168.11.186,192.168.11.187\t48391,3306\tDec 7, 2016 13:26:25.13 ...

  4. 监控Nginx负载均衡器脚本

    1.编写脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 vim nginx_pid.sh #!/bin/bash while  : do nginxpid=`ps -C ...

  5. 通过 Azure 媒体管理门户开始使用直播流媒体

    Jason Suess Azure媒体服务首席项目经理 几个月前,我们宣布发布 Azure媒体服务直播服务的公共预览版.其实这些直播服务早已被美国国家广播公司体育台用于多项重大体育赛事的多平台直播 ...

  6. struts1.3异常处理机制

    当Struts的控制器捕获到异常时,在异常处理块中,创建描述异常信息的ActionMessage对象,并放入ActionMessages对象中.然后把ActionMessages对象存放到一定范围内( ...

  7. ES5新特性:理解 Array 中增强的 9 个 API

    为了更方便的对JS中Array进行操作,ES5规范在Array的原型上新增了9个方法,分别是forEach.filter.map.reduce.reduceRight.some.every.index ...

  8. bugfree如何修改Bug7种解决方案的标注方法

    Bug有7种解决方案的标注方法 By Design- 就是这么设计的,无效的Bug Duplicate - 这个问题别人已经发现了,重复的Bug External - 是个外部因素(比如浏览器.操作系 ...

  9. [Andrew]Ext.net Grid常用js

    var gridFunction= function (gridId) {                //获取当前Grid                var gridView = Ext.ge ...

  10. Prism简介

    Prism是由微软Patterns & Practices团队开发的项目,目的在于帮助开发人员构建松散耦合的.更灵活.更易于维护并且更易于测试的WPF应用或是Silverlight应用以及Wi ...