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. php 数组指针相关函数current(),next(),prev(),end()

    mixed current(array target_array) current()函数返回位于target_array数组当前指针位置的数组值.与next().prev().和end()函数不同, ...

  2. linux下查看机器的硬件信息:

    查看CPU信息(型号) # cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c       8  Intel(R) Xeon(R) CPU    ...

  3. Dojo Widget系统(转)

    Dojo 里所有的小部件(Widget)都会直接或间接的继承 dijit._Widget / dijit._WidgetBase dijit._Widget 是 dojo 1.6 和 1.6之前的版本 ...

  4. HDU 2493 Timer 数学(二分+积分)

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2493 题意:给你一个圆锥,水平放置,圆锥中心轴与地面平行,将圆锥装满水,在圆锥某一表面开一个小洞,流出来 ...

  5. /MD, /MDD, /ML, /MT,/MTD(使用运行时库)

    1. VC编译选项 多线程(/MT)多线程调试(/MTd)多线程 DLL (/MD)多线程调试 DLL (/MDd) 2. C 运行时库                                 ...

  6. linux模块安装卸载命令

    lsmod   查看系统安装了那些模块 insmod 安装模块 rmmod 卸载模块 modprobe可安装模块,也可卸载模块 modprobe [-acdlrtvV][--help][模块文件][符 ...

  7. udelay、mdelay、ndelay、msleep使用比较说明

    时间单位:    毫秒(ms).微秒 (μs).纳秒(ns).皮秒(ps).飞秒(fs).阿秒.渺秒    1 s = 10^3 ms = 10^6 us = 10^9 ns = 10^12 ps = ...

  8. Android开发UI之ViewPager及PagerAdapter

    ViewPager,官网链接--http://developer.android.com/reference/android/support/v4/view/ViewPager.html ViewPa ...

  9. VM8下安装Mac OS X 10.7

    下载Mac OS X  10.7 安装包http://115.com/file/clj1iu8m#            下载HJMac http://115.com/file/cljyu1rh#   ...

  10. [转] 网络流算法--Ford-Fulkerson方法及其多种实现

    网络流 转载自:http://www.cnblogs.com/luweiseu/archive/2012/07/14/2591573.html 在上一章中我们讨论的主题是图中顶点之间的最短路径,例如公 ...