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进程。
举个样例。如果集群中一開始没有进程。
- 进程A1被创建,在/Monitor创建/Monitor/proc-1路径,因为/Monitor下仅仅有一个路径,A1被提升为Master进程。
- 进程A2被创建,在/Monitor创建/Monitor/proc-2路径。选主不成功,作为Slave进程;同一时候A1监控/Monitor的子节点变化事件。会收到有新进程被创建 。因此运行show_list。
- 进程A2被创建,在/Monitor创建/Monitor/proc-3路径。选主不成功。作为Slave进程。同一时候A1监控/Monitor的子节点变化事件,会收到有新进程被创建 ,因此运行show_list。
- 进程A1被Killed掉。其它进程监控到/Monitor的子节点变化事件,尝试选主,仅仅有A2序号成功,因此A2选主成功。A3作为Slave进程。
- 进程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选举的更多相关文章
- 【2】基于zookeeper,quartz,rocketMQ实现集群化定时系统
<一>项目结构图 (1)ZK协调分配 ===>集群中的每一个定时服务器与zookeeper交互,由集群中的master节点进行任务划分,并将划分结果分配给集群中的各个服务器节点. = ...
- vivo 容器集群监控系统架构与实践
vivo 互联网服务器团队-YuanPeng 一.概述 从容器技术的推广以及 Kubernetes成为容器调度管理领域的事实标准开始,云原生的理念和技术架构体系逐渐在生产环境中得到了越来越广泛的应用实 ...
- 分布式协调服务Zookeeper集群监控JMX和ZkWeb应用对比
分布式协调服务Zookeeper集群监控JMX和ZkWeb应用对比 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. JMX是用来远程监控Java应用的框架,这个也可以用来监控其他的J ...
- Zookeeper Monitor集群监控开发
随着线上越来越多的系统依赖Zookeeper集群.以至于Zookeeper集群的执行状况越来越重要.可是眼下还没有什么好用的Zookeeper集群监控系统(淘宝开源了一个Zookeeper监控系统,可 ...
- Zookeeper实现分布式集群监控
Zookeeepr实现分布式集群监控 Zookeeper中节点有两种:临时节点和永久节点 从类型上看节点又可以分为四种节点类型:PERSIST,PERSIST_SEQUENTIAL,EPHEMERAL ...
- kafka集群监控之kafka-manager部署(kafka-manager的进程为:ProdServerStart)
kafka集群监控之kafka-manager部署(ProdServerStart) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 雅虎官网GitHub项目:https://git ...
- zookeeper集群搭建及Leader选举算法源码解析
第一章.zookeeper概述 一.zookeeper 简介 zookeeper 是一个开源的分布式应用程序协调服务器,是 Hadoop 的重要组件. zooKeeper 是一个分布式的,开放源码的分 ...
- SpringCloud (十) Hystrix Dashboard单体监控、集群监控、与消息代理结合
一.前言 Dashboard又称为仪表盘,是用来监控项目的执行情况的,本文旨在Dashboard的使用 分别为单体监控.集群监控.与消息代理结合. 代码请戳我的github 二.快速入门 新建一个Sp ...
- HBase 集群监控系统构建
HBase 集群监控系统构建 标签(空格分隔): Hbase 一, 集群为什么需要监控系统 总的来说是为了保证系统的稳定性,可靠性,可运维性.具体来说我认为有以下几点: 掌控集群的核心性能指标,了解集 ...
随机推荐
- 移动开发:初学 iOS-UIViewController 心得
初学 iOS,本文翻译了一些 iOS 官网上的 UIViewController 的知识点,如有不到位或不正确的地方,还请指正: 本文所介绍的内容的目标: 理解content view control ...
- C#语法中一个问号(?)和两个问号(??)的运算符是什么意思?
(1).C#语法中一个个问号(?)的运算符是指:可以为 null 的类型. MSDN上面的解释: 在处理数据库和其他包含不可赋值的元素的数据类型时,将 null 赋值给数值类型或布尔型以及日期类型的功 ...
- HDU3362+状态压缩
dp[ i ]表示该状态下得所需花费. /* 状态压缩dp dp[i] = min( dp[ i-j ]+cost[ j ] ); 由i-j的状态转到i的状态 */ #include<stdio ...
- Druid数据库连接池两种简单使用方式
阿里巴巴推出的国产数据库连接池,据网上测试对比,比目前的DBCP或C3P0数据库连接池性能更好 简单使用介绍 Druid与其他数据库连接池使用方法基本一样(与DBCP非常相似),将数据库的连接信息全部 ...
- HDOJ多校联合第五场
1001 Inversion 题意:求逆序对,然后交换k次相邻的两个数,使得剩下的逆序对最少. 分析:题目用到的结论是:数组中存在一对逆序对,那么可以通过交换相邻两个数使得逆序对减少1,交换k次,可以 ...
- SQLite入门与分析(八)---存储模型(2)
3.页面结构(page structure) 数据库文件分成固定大小的页面.SQLite通过B+tree模型来管理所有的页面.页面(page)分三种类型:要么是tree page,或者是overflo ...
- 157. Read N Characters Given Read4
题目: The API: int read4(char *buf) reads 4 characters at a time from a file. The return value is the ...
- 商务部公开微软持有的Android技术专利
微软与众多Android厂商签署了专利授权协议,但从来没有公开它持有多少项Android技术专利.出人意料的是,为了收购诺基亚手机业务,微软今年4月递交到中国商务部的文件中完整公开了它的Android ...
- OLEDB和ODBC的区别(优缺点)
ODBC是一种连接数据库的开放标准,OLEDB(对象链接和嵌入数据库)位于ODBC层与应用程序之间. 在你的ASP页面里,ADO是位于OLEDB之上的应用程序. 你的ADO调用先被送到OLEDB,然后 ...
- Caused by: java.lang.ClassNotFoundException: javax.persistence.EntityListeners
Answer: This seems to be caused by Hibernate 3.6. It is now dependent on JPA, so it must have a JPA ...