Zookeeper C API
客户端使用C语言开发,zookeeper提供了两个库,zookeeper_st(单线程库)以及zookeeper_mt(多线程库)。
zookeeper_st提供了异步API和集成在应用程序用来实现事件循环的回调函数,该库是为了支持pthread库不支持或是不稳定的系统而存在。使用过程中需要通过zoo_interest以及zoo_process实现事件处理以及通知机制。
一般情况下使用zookeeper_mt多线程库,多线程库分为三个线程:主线程、io线程以及completion线程。主线程就是调用API的线程,io线程负责网络通信,而对于异步请求以及watcher的响应,io线程会发送给completion线程完成处理。
回调函数
Zookeeper C API中的各种回调函数原型如下:
监视函数(watcher funciton)原型
typedef void (*watcher_fn)(zhandle_t *zh, int type, int state, const char *path,void *watcherCtx);
- 1
监视函数参数
- zh:zookeeper句柄
- type:事件类型(event type),*_EVENT常量之一(后面会提到)
- state:连接状态(connection state),*_STATE常量之一(后面会提到)
- path:触发监视事件zonode节点的路径,如果为NULL,则事件类型为ZOO_SESSION_EVENT
- watcherCtx:监视器上下文
其他回调函数原型
Zookeeper 中还有几种在异步 API(一般以 zoo_a*开头的函数) 中使用的回调函数,根据回调函数处理异步函数返回值类型的不同分为以下几类:
- 处理返回 void 类型的回调函数
- 处理返回 Stat 结构的回调函数
- 处理返回字符串的回调函数
- 处理返回数据的回调函数
- 处理返回字符串列表(a list of string)的回调函数
- 同时处理返回字符串列表(a list of string)和 Stat 结构的回调函数
- 处理返回 ACL 信息的回调函数
具体如下所示:
// 处理返回 void 类型的回调函数
typedef void(* void_completion_t)(int rc, const void *data);
- 1
- 2
其中rc是异步返回的错误码,data是传入回调函数的自定义参数(下同)。
// 处理返回 Stat 结构的回调函数
typedef void(* stat_completion_t)(int rc, const struct Stat *stat, const void *data);
- 1
- 2
其中stat指向与znode相关的Stat信息(下同)。
// 处理返回字符串的回调函数
typedef void(* string_completion_t)(int rc, const char *value, const void *data);
- 1
- 2
其中value返回的字符串。
// 处理返回数据的回调函数
typedef void(* data_completion_t)(int rc, const char *value, int value_len, const struct Stat *stat, const void *data);
- 1
- 2
其中value表示返回的字节数组,value_len是字节长度。
// 处理返回字符串列表(a list of string)的回调函数
typedef void(* strings_completion_t)(int rc, const struct String_vector *strings, const void *data);
- 1
- 2
- 3
其中strings指向了某个znode节点的所有子节点名称列表结构(下同)。
// 同时处理返回字符串列表(a list of string)和 Stat 结构的回调函数
typedef void(* strings_stat_completion_t)(int rc, const struct String_vector *strings, const struct Stat *stat, const void *data);
- 1
- 2
// 处理以及返回 ACL 信息的回调函数
typedef void(* acl_completion_t)(int rc, struct ACL_vector *acl, struct Stat *stat, const void *data);
- 1
- 2
其中acl指向包含某个节点ACL信息的指针。
常用API函数
初始化、销毁Zookeeper句柄
ZOOAPI zhandle_t *zookeeper_init(const char *host, watcher_fn fn,
int recv_timeout,
const clientid_t * clientid,
void *context, int flags);
- 1
- 2
- 3
- 4
参数:
- host:逗号隔开的 host:port 对, 每个代表一个 zk server, 例如: “127.0.0.1:3000,127.0.0.1:3001”
- fn:全局的监视器回调函数,当发生事件通知时,该函数会被调用
- clientid:客户端尝试重连的先前会话的ID,如果不需要重连先前的会话,则设置为0。客户端可以通过调用 zoo_client_id来访问一个已经连接上的并且有效的会话ID,如果clientid对应的会话超时,或者由于某种原因 clientid变为无效了,那么zookeeper_init将返回一个非法的 zhandle_t,通过 zhandle_t 的状态可以获知 zookeeper_init 调用失败的原因(通常为 ZOO_EXPIRED_SESSION_STATE)
- context:与zhandle_t实例相关联的“上下文对象”(可以通过该参数为 zhandle_t 传入自定义类型的数据),应用程序可以通过 zoo_get_context 访问它(例如在监视器回调函数中),当然 zookeeper 内部没有用到该参数,所以 context 可以设置为 NULL。
flags:一般设置为0
ZOOAPI int zookeeper_close(zhandle_t * zh);
- 1
辅助函数
ZOOAPI void zoo_set_debug_level(ZooLogLevel logLevel);
ZOOAPI void zoo_set_log_stream(FILE * logStream);
ZOOAPI struct sockaddr *zookeeper_get_connected_host(zhandle_t * zh, struct sockaddr
*addr,
socklen_t * addr_len);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
ZOOAPI int zookeeper_interest(zhandle_t * zh, int *fd, int *interest,
struct timeval *tv);
- 1
- 2
返回向zookeeper中注册的事件列表
参数:
- fd:文件描述列表
- interest:事件类型,ZOOKEEPER_WRITE、ZOOKEEPER_READ标志
- select或是poll超时时间
ZOOAPI int zookeeper_process(zhandle_t * zh, int events);
- 1
通知zookeeper注册的事件已经发生
参数:
- events:ZOOKEEPER_WRITE、ZOOKEEPER_READ标志
ZOOAPI int zoo_state(zhandle_t * zh);
- 1
获取zookeeper连接的状态信息
同步接口
创建、删除节点
ZOOAPI int zoo_create(zhandle_t * zh, const char *path,
const char *value, int valuelen,
const struct ACL_vector *acl, int flags,
char *path_buffer, int path_buffer_len);
- 1
- 2
- 3
- 4
参数:
- zh:zookeeper_init返回的zookeeper句柄
- path:节点路径
- value:该节点保存的数据
- valuelen:该节点保存数据的大小。如果 value 被设置为 NULL(该 znode 节点不包含数据),则 valuelen 应该设置为 -1
- acl:该节点初始 ACL,ACL 不能为null 或空
- flags:该参数可以设置为 0,或者创建标识符 ZOO_EPHEMERAL, ZOO_SEQUENCE 的组合或(OR)
- path_buffer:用于保存返回节点新路径(因为设置了 ZOO_SEQUENCE 后 zoo_create 所创建的节点名称与参数 path 提供的名称不同,新的节点名称后面填充了序号),path 字符串以 NULL 结束。path_buffer 可以设置为 NULL,此时 path_buffer_len 等于 0
- path_buffer_len:path_buffer的长度,如果新节点名称的长度大于path_buffer_len,则节点名称将会被截断,而服务器端该节点的名称不会截断
ZOOAPI int zoo_delete(zhandle_t * zh, const char *path, int version);
- 1
参数:
- version:节点的版本号,如果该 znode 节点的实际版本号与该参数提供的版本号不一值,则删除节点失败,如果 version 为 -1,则不做版本检查
检查节点状态
两者的区别就是后者可以指定单独的watcher_fn(监视器回调函数),前者只能用使用zookeeper_init设置的全局监视器回调函数,下同。
ZOOAPI int zoo_exists(zhandle_t * zh, const char *path, int watch,
struct Stat *stat);
- 1
- 2
参数(下同):
- watch:如果非 0,则在服务器端设置监视,当节点发生变化时客户端会得到通知,即使当前指定的节点不存在也会设置监视,这样该节点被创建时,客户端也可以得到通知
- stat:返回的 Stat 信息
ZOOAPI int zoo_wexists(zhandle_t * zh, const char *path,
watcher_fn watcher, void *watcherCtx,
struct Stat *stat);
- 1
- 2
- 3
参数(下同):
- watcher:如果不为 NULL 则会在服务器端设置监视,当节点发生变化时客户端会得到通知,即使当前指定的节点不存在也会设置监视,这样该节点被创建时,客户端也可以得到通知
- watchCtx:用户指定的数据,将被传入到监视器回调函数中,与由 zookeeper_init() 设置的全局监视器上下文不同,该函数设置的监视器上下文只与当前的监视器相关联
获取节点数据
ZOOAPI int zoo_get(zhandle_t * zh, const char *path, int watch,
char *buffer, int *buffer_len, struct Stat *stat);
- 1
- 2
参数:
- buffer:用于保存从 zookeeper 服务器获取的节点数据
- buffer_len:buffer 大小,一旦成功返回该值将会被设置为节点数据的实际大小,如果节点的数据为空,则数据大小为 -1,buffer_len 也为 -1
ZOOAPI int zoo_wget(zhandle_t * zh, const char *path,
watcher_fn watcher, void *watcherCtx,
char *buffer, int *buffer_len, struct Stat *stat);
- 1
- 2
- 3
- 4
获取子节点列表
ZOOAPI int zoo_get_children(zhandle_t * zh, const char *path,
int watch, struct String_vector *strings);
- 1
- 2
参数(下同):
- strings:返回的各个子节点路径。这里strings在调用api时会通过malloc分配内存空间,将子节点所有的目录存放在data字段中,需要客户端调用deallocate_String_vector(strings)做释放处理。
ZOOAPI int zoo_wget_children(zhandle_t * zh, const char *path,
watcher_fn watcher, void *watcherCtx,
struct String_vector *strings);
ZOOAPI int zoo_get_children2(zhandle_t * zh, const char *path,
int watch, struct String_vector *strings,
struct Stat *stat);
ZOOAPI int zoo_wget_children2(zhandle_t * zh, const char *path,
watcher_fn watcher, void *watcherCtx,
struct String_vector *strings,
struct Stat *stat);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
异步接口
初学Zookeeper到这里肯定会有疑问,Watcher和AsyncCallBack之间的区别是什么?在介绍异步接口之前先来回顾一下Watcher和AsyncCallBack之间的区别和实现。
Watcher是用于监听节点、session状态的,比如get方法对节点设置了watcher之后,当节点的数据发生了改变之后,服务器会主动发送notification给客户端,然后进行watcher的回调。
AsyncCallBack是以异步的方式调用API,主动向服务器发送请求,然后将请求放入到pending队列中,等待服务器的响应。收到服务器对应的响应后,进行回调。
Zookeeper客户端中Watcher和AsyncCallback都是异步回调的方式,但它们回调的时机是不一样的,前者是由服务器发送事件触发客户端回调,后者是在执行了请求后得到响应后客户端主动触发的。它们的共同点在于都需要在获取了服务器响应之后,由io线程将事件注册到completion线程的事件队列中,然后由completion线程从队列中逐个取出并处理。
创建、删除节点
ZOOAPI int zoo_acreate(zhandle_t * zh, const char *path,
const char *value, int valuelen,
const struct ACL_vector *acl, int flags,
string_completion_t completion, const void *data);
- 1
- 2
- 3
- 4
其中参数 string_completion_t completion 即返回字符串的回调函数,那么当 zoo_acreate 调用结束时将会触发 completion 回调函数的调用,同时传递给 completion 的 rc 参数为: ZOK 操作完成;ZNONODE 父节点不存在;ZNODEEXISTS 节点已存在;ZNOAUTH 客户端没有权限创建节点。ZNOCHILDRENFOREPHEMERALS 临时节点不能创建子节点。而 string_completion_t completion 中 const char *value 参数即新节点的路径名(注:如果 zoo_acreate 设置了ZOO_SEQUENCE ,则创建节点成功后,节点名称并不是 zoo_acreate 中 path 参数所指定的名称,而是类似与 /xyz0000000001,/xyz0000000002… 的名称)。另外,string_completion_t completion 中 const void *data 参数即为 zoo_acreate 中的 const void *data。
参数(下同):
- zh:zookeeper_init() 返回的 zookeeper 句柄
- path:节点路径
- value:该节点保存的数据
- valuelen:该节点保存数据的大小
- acl:该节点初始 ACL,ACL 不能为null 或空
- flags:该参数可以设置为 0,或者创建标识符 ZOO_EPHEMERAL, ZOO_SEQUENCE 的组合或(OR)
- completion:当创建节点请求完成时会调用该函数
- data:completion 函数被调用时,传递给 completion 的数据
ZOOAPI int zoo_adelete(zhandle_t * zh, const char *path, int version,
void_completion_t completion, const void *data);
- 1
- 2
参数(下同):
- version:期望的节点版本号,如果真实的版本号与期望的版本号不同则 zoo_delete() 调用失败,-1 表示不不检查版本号。
ZOOAPI int zoo_aexists(zhandle_t * zh, const char *path, int watch,
stat_completion_t completion, const void *data);
- 1
- 2
参数:
- watch:如果非 0,则在服务器端设置监视,当节点发生变化时客户端会得到通知,即使当前指定的节点不存在也会设置监视,这样该节点被创建时,客户端也可以得到通知
ZOOAPI int zoo_awexists(zhandle_t * zh, const char *path,
watcher_fn watcher, void *watcherCtx,
stat_completion_t completion, const void *data);
- 1
- 2
- 3
参数(下同):
- watcher:如果非 0,则在服务器端设置监视,当节点发生变化时客户端会得到通知,即使当前指定的节点不存在也会设置监视,这样该节点被创建时,客户端也可以得到通知
- watcherCtx:用户指定的数据,将被传入到监视器回调函数中,与由 zookeeper_init() 设置的全局监视器上下文不同,该函数设置的监视器上下文只与当前的监视器相关联
ZOOAPI int zoo_aget(zhandle_t * zh, const char *path, int watch,
data_completion_t completion, const void *data);
- 1
- 2
参数(下同):
- 如果非 0,则在服务器端设置监视,当节点发生变化时客户端会得到通知。
ZOOAPI int zoo_awget(zhandle_t * zh, const char *path,
watcher_fn watcher, void *watcherCtx,
data_completion_t completion, const void *data);
ZOOAPI int zoo_aset(zhandle_t * zh, const char *path,
const char *buffer, int buflen, int version,
stat_completion_t completion, const void *data);
ZOOAPI int zoo_aget_children(zhandle_t * zh, const char *path,
int watch,
strings_completion_t completion,
const void *data);
ZOOAPI int zoo_awget_children(zhandle_t * zh, const char *path,
watcher_fn watcher, void *watcherCtx,
strings_completion_t completion,
const void *data);
ZOOAPI int zoo_aget_children2(zhandle_t * zh, const char *path,
int watch,
strings_stat_completion_t completion,
const void *data);
ZOOAPI int zoo_awget_children2(zhandle_t * zh, const char *path,
watcher_fn watcher, void *watcherCtx,
strings_stat_completion_t completion,
const void *data);
ZOOAPI int zoo_async(zhandle_t * zh, const char *path,
string_completion_t completion, const void *data);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
节点状态信息
使用get命令获取指定节点数据时,也会返回节点的状态信息Stat,该结构包含如下字段:
- czxid:节点创建时的zxid
- mzxid:节点最新一次更新发生时的zxid
- ctime:节点创建时的时间戳
- time:节点最新一次更新发生时的时间戳
- dataVersion:节点数据的更新次数
- cversion:其子节点的更新次数
- aclVersion:节点ACL(授权信息)的更新次数
- ephemeralOwner:如果该节点为ephemeral节点, ephemeralOwner值表示与该节点绑定的session id. 如果该节点不是ephemeral节点, ephemeralOwner值为0
- dataLength:节点数据的字节数
- numChildren:子节点个数
常见错误码
错误码 | 说明 |
---|---|
ZOK | 正常返回 |
ZSYSTEMERROR | 系统或服务器端错误(System and server-side errors),服务器不会抛出该错误,该错误也只是用来标识错误范围的,即大于该错误值,且小于 ZAPIERROR 都是系统错误 |
ZRUNTIMEINCONSISTENCY | 运行时非一致性错误 |
ZDATAINCONSISTENCY | 数据非一致性错误 |
ZCONNECTIONLOSS | Zookeeper 客户端与服务器端失去连接 |
ZMARSHALLINGERROR | 在 marshalling 和 unmarshalling 数据时出现错误(Error while marshalling or unmarshalling data) |
ZUNIMPLEMENTED | 该操作未实现(Operation is unimplemented) |
ZOPERATIONTIMEOUT | 该操作超时(Operation timeout) |
ZBADARGUMENTS | 非法参数错误(Invalid arguments) |
ZINVALIDSTATE | 非法句柄状态(Invliad zhandle state) |
ZAPIERROR | API 错误(API errors),服务器不会抛出该错误,该错误也只是用来标识错误范围的,错误值大于该值的标识 API 错误,而小于该值的标识 ZSYSTEMERROR |
ZNONODE | 节点不存在(Node does not exist) |
ZNOAUTH | 没有经过授权(Not authenticated) |
ZBADVERSION | 版本冲突(Version conflict) |
ZNOCHILDRENFOREPHEMERALS | 临时节点不能拥有子节点(Ephemeral nodes may not have children) |
ZNODEEXISTS | 节点已经存在(The node already exists) |
ZNOTEMPTY | 该节点具有自身的子节点(The node has children) |
ZSESSIONEXPIRED | 会话过期(The session has been expired by the server) |
ZINVALIDCALLBACK | 非法的回调函数(Invalid callback specified) |
ZINVALIDACL | 非法的ACL(Invalid ACL specified) |
ZAUTHFAILED | 客户端授权失败(Client authentication failed) |
ZCLOSING | Zookeeper 连接关闭(ZooKeeper is closing) |
ZNOTHING | 并非错误,客户端不需要处理服务器的响应(not error, no server responses to process) |
ZSESSIONMOVED | 会话转移至其他服务器,所以操作被忽略(session moved to another server, so operation is ignored) |
Watcher通知的状态类型和事件类型
状态类型(state)
状态码 | 说明 |
---|---|
-112 | 会话超时(ZOO_EXPIRED_SESSION_STATE) |
-113 | 认证失败(ZOO_AUTH_FAILED_STATE) |
1 | 连接建立中(ZOO_CONNECTING_STATE) |
2 | 连接建立中(ZOO_ASSOCIATING_STATE) |
3 | 连接已建立(ZOO_CONNECTED_STATE) |
999 | 无连接状态 |
事件类型(type)
事件码 | 说明 |
---|---|
1 | 创建节点事件(ZOO_CREATED_EVENT) |
2 | 删除节点事件(ZOO_DELETED_EVENT) |
3 | 更改节点事件(ZOO_CHANGED_EVENT) |
4 | 子节点列表变化事件(ZOO_CHILD_EVENT) |
-1 | 会话session事件(ZOO_SESSION_EVENT) |
-2 | 监视被移除事件(ZOO_NOTWATCHING_EVENT) |
获取Watcher
Watcher的设置和获取在开发中很常见,不同的操作会收到不同的watcher信息。对父节点的变更以及孙节点的变更都不会触发watcher,而对watcher本身节点以及子节点的变更会触发watcher。
exists、getdata以及getchildren方法获取watcher可参考如下:
操作 | 方法 | 触发watcher | watcher state | watcher type | watcher path |
---|---|---|---|---|---|
Create当前节点 | getdata | × | × | × | × |
getchildren | √ | 3 | 4 | √ | |
exists | × | × | × | × | |
set当前节点 | getdata | √ | 3 | 3 | √ |
getchildren | × | × | × | × | |
exists | √ | 3 | 3 | √ | |
delete当前节点 | getdata | √ | 3 | 2 | √ |
getchildren | √ | 3 | 2 | √ | |
exists | √ | 3 | 2 | √ | |
create子节点 | getdata | × | × | × | × |
getchildren | √ | 3 | 4 | √ | |
exists | × | × | × | × | |
set子节点 | getdata | × | × | × | × |
getchildren | × | × | × | × | |
exists | × | × | × | × | |
delete子节点 | getdata | × | × | × | × |
getchildren | √ | 3 | 4 | √ | |
exists | × | × | × | × | |
恢复连接 | getdata | √ | 1 | -1 | × |
getchildren | √ | 1 | -1 | × | |
exists | √ | 1 | -1 | × | |
恢复连接session未超时 | getdata | √ | -112 | -1 | × |
getchildren | √ | -112 | -1 | × | |
exists | √ | -112 | -1 | × | |
恢复连接session超时 | getdata | √ | 3 | -1 | × |
getchildren | √ | 3 | -1 | × | |
exists | √ | 3 | -1 | × |
单线程库使用示例
主函数
int main(int argc, const char *argv[]) {
/* 设置zookeeper host,其中host字符串格式为逗号隔开的IP:PORT对 */
static const char* host = "x.x.x.x:x";
/* 设置客户端连接服务器超时时间,单位ms */
int timeout = 30000;
/* 设置日志为调试级别 */
zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG);
/* 用于启用或停用quarum端点的随机化排序,通常仅在测试时使用。
如果非0,使得client连接到quarum端按照被初始化的顺序。
如果是0,zookeeper_init将变更端点顺序,使得client连接分布在更优的端点上。*/
zoo_deterministic_conn_order(1);
/* 初始化zookeeper句柄,传入全局监视器回调函数、超时、以及上下文信息 */
zhandle_t* zkhandle = zookeeper_init(host, zk_watcher_g, 30000, 0, (void *)"hello world", 0);
if (zkhandle == NULL) {
fprintf(stderr, "Error when connecting to zookeeper servers...\n");
exit(EXIT_FAILURE);
}
/* 等待异步回调 */
int fd, interest, events;
int rc;
fd_set rfds, wfds, efds;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&efds);
while (1) {
struct timeval tv;
/* 注册zookeeper中的事件 */
zookeeper_interest(zkhandle, &fd, &interest, &tv);
if (fd != -1) {
if (interest & ZOOKEEPER_READ) {
FD_SET(fd, &rfds);
} else {
FD_CLR(fd, &rfds);
}
if (interest & ZOOKEEPER_WRITE) {
FD_SET(fd, &wfds);
} else {
FD_CLR(fd, &wfds);
}
} else {
fd = 0;
}
if (select(fd + 1, &rfds, &wfds, &efds, &tv) < 0) {
printf("[%s %d]select failed, err=%d, msg=%s\n", __FUNCTION__, __LINE__,
errno, strerror(errno));
}
events = 0;
if (FD_ISSET(fd, &rfds)) {
events |= ZOOKEEPER_READ;
}
if (FD_ISSET(fd, &wfds)) {
events |= ZOOKEEPER_WRITE;
}
/* 通知zookeeper客户端事件已经发生 */
zookeeper_process(zkhandle, events);
}
zookeeper_close(zkhandle);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
全局监视器回调函数
void zk_watcher_g(zhandle_t *zh, int type, int state, const char* path, void *watcherCtx) {
int ret;
const clientid_t *zk_clientid;
/* 声明节点路径 */
if(path == NULL || strlen(path) == 0) {
path = "/xyz";
}
/* 当前event type是会话 */
if(type == ZOO_SESSION_EVENT) {
if(state == ZOO_EXPIRED_SESSION_STATE) {
printf("[%s %d] zookeeper session expired\n", __FUNCTION__, __LINE__);
}
/* 会话状态是已连接 */
else if(state == ZOO_CONNECTED_STATE) {
/* 获取客户端的 session id,只有在客户端的当前连接状态有效时才可以 */
zk_clientid = zoo_client_id(zh);
/* 调用异步api */
ret = zoo_aexists(zh, path, TRUE, zk_stat_completion, path);
ret = zoo_aget(zh, path, TRUE, zk_data_completion, "get param");
ret = zoo_aget_children2(zh, path, TRUE, zk_strings_stat_completion, "get children param");
}
}
/* 当前event type是节点创建事件 */
else if (type == ZOO_CREATED_EVENT) {
...
}
/* 当前event type是节点删除事件 */
else if (type == ZOO_DELETED_EVENT) {
...
}
/* 当前event type是节点数据改变事件 */
else if (type == ZOO_CHANGED_EVENT) {
...
}
/* 当前event type是子节点事件 */
else if (type == ZOO_CHILD_EVENT) {
...
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
程序执行分析
单线程程序链接需要libzookeeper_st库
Something happened.
type: SESSION_EVENT
state: 4261760s
path:
watcherCtx: hello world
[zk_watcher_g 140] connected to zookeeper server with clientid=96408539334299438
in stat completion rc = 0 data = /xyz stat:
tctime = Sun Nov 20 10:12:08 2016
| tczxid = 609cd7384
tmtime = Sun Nov 20 10:12:08 2016
| tmzxid = 609cd7384
version = 0in data completion []: rc = 0
tctime = Sun Nov 20 10:12:08 2016
| tczxid = 609cd7384
tmtime = Sun Nov 20 10:12:08 2016
| tmzxid = 609cd7384
version = 0in strings stat completion [get children param]: rc = 0, string count 0
tctime = Sun Nov 20 10:12:08 2016
| tczxid = 609cd7384
tmtime = Sun Nov 20 10:12:08 2016
| tmzxid = 609cd7384
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
在程序执行前首先建立/xyz节点,服务器首次返回的事件类型为SESSION_EVENT,显示客户端连接已成功。
version = 0Something happened.
type: CHILD_EVENT
state: 4261760s
path: /xyz
watcherCtx: hello world
path (null) child event
in data completion []: rc = 0
tctime = Sun Nov 20 10:12:08 2016
| tczxid = 609cd7384
tmtime = Sun Nov 20 10:12:08 2016
| tmzxid = 609cd7384
version = 0in strings stat completion [get children param]: rc = 0, string count 1
0: abc
tctime = Sun Nov 20 10:12:08 2016
| tczxid = 609cd7384
tmtime = Sun Nov 20 10:12:08 2016
| tmzxid = 609cd7384
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
创建节点/xyz/abc,服务器返回的事件类型是CHILD_EVENT,表示的是子节点事件,同时返回子节点列表。
version = 0Something happened.
type: CHANGED_EVENT
state: 4261760s
path: /xyz
watcherCtx: hello world
path /xyz changed
in strings stat completion [get children param]: rc = 0, string count 1
0: abc
tctime = Sun Nov 20 10:12:08 2016
| tczxid = 609cd7384
tmtime = Sun Nov 20 10:16:56 2016
| tmzxid = 609cd7449
version = 1in data completion []: rc = 0
tctime = Sun Nov 20 10:12:08 2016
| tczxid = 609cd7384
tmtime = Sun Nov 20 10:16:56 2016
| tmzxid = 609cd7449
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
设置节点数据/xyz,服务器返回的事件类型是CHANGED_EVENT,表示的节点数据改变事件,
多线程库使用示例
多线程库api使用相对单线程就比较简单了
主函数
int main(int argc, const char *argv[]) {
if (argc != 2) {
printf("usage: zookeeper_m_test server_address\n");
exit(-1);
}
const char* host = argv[1];
int timeout = 30000;
char buffer[512];
int *bufferlen;
zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG);
/* 初始化zookeeper句柄,传入全局监视器回调函数、超时、以及简单参数 */
zhandle_t* zkhandle = zookeeper_init(host, zookeeper_watcher_g, timeout, 0,
NULL, 0);
if (zkhandle == NULL) {
fprintf(stderr, "Error when connecting to zookeeper servers...\n");
exit(EXIT_FAILURE);
}
/* 调用api */
create(zkhandle, "/abc");
exists(zkhandle, "/abc");
getACL(zkhandle, "/abc");
del(zkhandle, "/abc");
while (1)
;
zookeeper_close(zkhandle);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
全局监视器回调函数
和上面单线程类似
程序执行分析
多线程程序需要链接libzookeeper_mt以及pthread两个库
Something happened.
type: -1
state: 3
path:
watcherCtx: hello zookeeper.
[acreate]: rc = -110
aexists: rc = 0 Stat:
ctime = Sun Nov 20 00:25:19 2016
czxid=609ccd4de
mtime=Sun Nov 20 00:26:04 2016
mzxid=609ccd4e9
version=1 aversion=0
ephemeralOwner = 0
...
Something happened.
type: 3
state: 3
path: /xyz
watcherCtx: hello zookeeper.
...
[adelete]: rc = 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
这里可以看到创建session成功的时候,服务器返回状态码state=3,事件码type=-1。
而改变/xyz节点数据的时候,服务器返回状态码state=3,事件码type=3(由于这里设置了watcher,因此当节点改变的时候会通知一次客户端)。
最后成功删除节点/xyz。
参考
http://blog.jobbole.com/104833/
http://blog.youkuaiyun.com/wudongxu/article/details/9198963
http://www.cnblogs.com/haippy/archive/2013/02/21/2920426.html
http://www.cnblogs.com/haippy/archive/2013/02/24/2924567.html
http://www.cnblogs.com/haippy/archive/2013/02/21/2920426.html
http://blog.youkuaiyun.com/luojinbai/article/details/48207893