zookeeper
应用场景
-
统一命名服务:就是和nginx 一样的功能,你输入这个域名,我根据负载均衡去找到相应的服务器IP地址
-
统一配置管理:配置文件同步,让所有需要这个配置文件的中间件kafka等等去监听zookeeper上的文件,发生变化就通知
-
统一集群管理:zookeeper上的文件去监控客户端的变化,客户端一旦发生了变化,发么我就写到文件里
-
服务器节点动态上下线:看图
-
软负载均衡
配置参数
#通信心跳,每隔多久发一次信号 tickTime=2000 #第一次建立连接需要的时间,就是initLimit*tickTime,超过这个时间就启动失败 initLimit=10 # The number of ticks that can pass between # sending a request and getting an acknowledgement ## 通信同步时间,syncLmit*tickTime就是最大的通信时长,超过这个时间还没有通信就是这个节点宕机了 syncLimit=5 # the directory where the snapshot is stored. # do not use /tmp for storage, /tmp here is just # example sakes. dataDir=/opt/zookeeper/server2/apache-zookeeper-3.5.7-bin/data dataLogDir=/opt/zookeeper/server2/apache-zookeeper-3.5.7-bin/logData # the port at which the clients will connect clientPort=2182 # the maximum number of client connections. # increase this if you need to handle more clients #maxClientCnxns=60 # # Be sure to read the maintenance section of the # administrator guide before turning on autopurge. # # http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance # # The number of snapshots to retain in dataDir #autopurge.snapRetainCount=3 # Purge task interval in hours # Set to "0" to disable auto purge feature #autopurge.purgeInterval=1 server.1=127.0.0.1:2881:3881 server.2=127.0.0.1:2882:3882 server.3=127.0.0.1:2883:3883
刚开始配置的时候,启动一个服务器,结果报错了,因为集群半数的服务器没有启动,所有全部启动就好了
投票机制
-
SID:服务器ID。用来唯一标识一台ZooKeeper集群中的机器,每台机器不能重复,和myid一致。
-
ZXID:事务ID。ZXID是一个事务ID,用来标识一次服务器状态的变更。在某一时刻,集群中的每台机器的ZXID值不一定完全一致,这和ZooKeeper服务器对于客户端“更新请求”的处理逻辑有关。
-
Epoch:每个Leader任期的代号。没有Leader时同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加
两种情况:
-
第一次启动:只看myid
-
无法和leader保持连接:先看epoch,再看zxid,再看myid
-
leader挂了
-
没有得到半数follow的统一
-
节点的信息
[zk: localhost:2181(CONNECTED) 1] ls -s / [zookeeper]cZxid = 0x0 # 创建节点的事务ID ctime = Thu Jan 01 08:00:00 CST 1970 # 节点被创建的时间 mZxid = 0x0 # 最后一次更新这个节点的事务ID mtime = Thu Jan 01 08:00:00 CST 1970 # 最后一次修改这个节点的时间 pZxid = 0x0 # 这个节点的最后更新的子节点的事务ID cversion = -1 # 子节点的变化次数 dataVersion = 0 # 节点数据变化号 aclVersion = 0 # 访问控制列表的变化号 ephemeralOwner = 0x0 # 临时节点的seenion id 不是临时节点就是0 dataLength = 0 # 数据大小 numChildren = 1 # 多少子节点
节点类型:持久和短暂,有序号和无序号
持久和短暂:客户端和服务端断开连接之后,这个节点还在不在了
有序号和无序号:很好理解了,这个序号又父节点,单调递增,这个就是方便了排序
持久节点带序号的:正常创建node1
持久节点不带序号:客户端与Zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号 node1_001
临时不带序号的:创建完删除就行了
临时带序号的:删除之后这个节点XXXXXXXX
#持久节点 #-s 就是有序号的 #-e 就是临时节点 # create创建 # get 获取当前值 # set 修改当前值 [zk: localhost:2181(CONNECTED) 2] ls / [zookeeper] [zk: localhost:2181(CONNECTED) 3] create /sanguo "diaochan" Created /sanguo [zk: localhost:2181(CONNECTED) 4] ls / [sanguo, zookeeper] [zk: localhost:2181(CONNECTED) 5] create /sanguo/shuguo Created /sanguo/shuguo [zk: localhost:2181(CONNECTED) 6] create /sanguo/wuguo Created /sanguo/wuguo [zk: localhost:2181(CONNECTED) 7] create /sanguo/weiguo Created /sanguo/weiguo [zk: localhost:2181(CONNECTED) 8] ls / [sanguo, zookeeper] [zk: localhost:2181(CONNECTED) 9] ls /sanguo [shuguo, weiguo, wuguo] [zk: localhost:2181(CONNECTED) 1] get /sanguo diaochan
监听器:
1、首先要有一个main(线程
2、在main线程中创建Zookeeper客户端, 这时就会创建两个线程,一个负责网络连接通信(connet) ,一个负责监听(listener) 。
3、通过connect线程将注册的监听事件发送给Zookeeper。
4、在Zookeeper的注册监听器列表中将注册的监听事件添加到列表中。
5、Zookeeper监听到有 数据或路径变化,就会将这个消息发送给listener线程。
6、listener线程内部调用了process0方法。
常见的监听:
-
节点数据的变化 get -w 路径
-
节点增删的变化 ls -w 路径
代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <zookeeper/zookeeper.h> //#include <zookeeper/zookeeper_log.h> void zktest_watcher_g(zhandle_t* zh, int type, int state, const char* path, void* watcherCtx) { printf("Something happened.\n"); printf("type: %d\n", type); printf("state: %d\n", state); printf("path: %s\n", path); printf("watcherCtx: %s\n", (char *)watcherCtx); } void zktest_dump_stat(const struct Stat *stat) { char tctimes[40]; char tmtimes[40]; time_t tctime; time_t tmtime; if (!stat) { fprintf(stderr,"null\n"); return; } tctime = stat->ctime/1000; tmtime = stat->mtime/1000; ctime_r(&tmtime, tmtimes); ctime_r(&tctime, tctimes); fprintf(stderr, "\tctime = %s\tczxid=%llx\n" "\tmtime=%s\tmzxid=%llx\n" "\tversion=%x\taversion=%x\n" "\tephemeralOwner = %llx\n", tctimes, stat->czxid, tmtimes, stat->mzxid, (unsigned int)stat->version, (unsigned int)stat->aversion, stat->ephemeralOwner); } //当 zoo_aexists 请求完成时会调用该函数 //rc参数为: ZOK 操作完成;ZNONODE 节点不存在;ZNOAUTH 客户端没有权限删除节点。 void zktest_stat_completion(int rc, const struct Stat *stat, const void *data) { fprintf(stderr, "%s: rc = %d Stat:\n", (char*)data, rc); zktest_dump_stat(stat); } void zktest_void_completion(int rc, const void *data) { fprintf(stderr, "[%s]: rc = %d\n", (char*)(data==0?"null":data), rc); } //当创建节点请求完成时会调用该函数 //rc 参数为: ZOK 操作完成;ZNONODE 父节点不存在;ZNODEEXISTS 节点已存在;ZNOAUTH 客户端没有权限创建节点。ZNOCHILDRENFOREPHEMERALS 临时节点不能创建子节点 //value 参数即新节点的路径名 //string_completion_t completion 中 const void *data 参数即为 zoo_acreate 中的 const void *data。 void zktest_string_completion(int rc, const char *name, const void *data) { fprintf(stderr, "[%s]: rc = %d\n", (char*)(data==0?"null":data), rc); if (!rc) { fprintf(stderr, "\tname = %s\n", name); } } int main(int argc, const char *argv[]) { const char* host = "127.0.0.1:2181"; //如果 30 秒内客户端没有连接上 Zookeeper 服务则表示连接超时。 int timeout = 30000; //设置日志等级。 zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); //初始化zookeeper句柄(zhandle_t) //第四个参数为客户端会话id,客户端尝试重连的先前会话的ID,如果不需要重连先前的会话,则设置为 0 //第五个参数为当前zk的上下文对象,最后一个参数是预留参数 zhandle_t* zkhandle = zookeeper_init(host, zktest_watcher_g, timeout, 0, (void * )"hello zookeeper.", 0); if (zkhandle == NULL) { fprintf(stderr, "Error when connecting to zookeeper servers...\n"); exit(EXIT_FAILURE); } // struct ACL ALL_ACL[] = {{ZOO_PERM_ALL, ZOO_ANYONE_ID_UNSAFE}}; // struct ACL_vector ALL_PERMS = {1, ALL_ACL}; //创建一个znode节点,节点路径为"/xyz",保存一个长度为5,内容为hello的数据,任何人都可以访问 //这个一个异步函数,调用之后,直接往下执行,但是此时节点并没有真正的创建成功。节点创建成功之后会调用zktest_string_compeltion函数 //最后一个参数是该回调函数,要传入的参数 int ret = zoo_acreate(zkhandle, "/xyz", "hello", 5, &ZOO_OPEN_ACL_UNSAFE, 0 /* ZOO_SEQUENCE */, zktest_string_completion, "acreate"); if (ret) { fprintf(stderr, "Error %d for %s\n", ret, "acreate"); exit(EXIT_FAILURE); } ret = 0; //第三个参数是监视,如果非 0,则在服务器端设置监视,当节点发生变化时客户端会得到通知,即使当前指定的节点不存在也会设置监视,这样该节点被创建时,客户端也可以得到通知。 ret = zoo_aexists(zkhandle, "/xyz", 1, zktest_stat_completion, "aexists"); if (ret) { fprintf(stderr, "Error %d for %s\n", ret, "aexists"); exit(EXIT_FAILURE); } ret = 0; // Wait for asynchronous zookeeper call done. getchar(); //第三个参数:期望的节点版本号,如果真实的版本号与期望的版本号不同则 zoo_delete() 调用失败,-1 表示不不检查版本号。 ret = zoo_adelete(zkhandle, "/xyz", -1, zktest_void_completion, "adelete"); if (ret) { fprintf(stderr, "Error %d for %s\n", ret, "adelete"); exit(EXIT_FAILURE); } // Wait for asynchronous zookeeper call done. getchar(); zookeeper_close(zkhandle); return 0; }