最新消息:比度技术-是关注云计算、大数据、分布式存储、高并发、高性能、人工智能等互联网技术的个人博客。

Zookeeper客户端

编程开发 bidu 110浏览

一、前言

在学习了Zookeeper相关的理论知识后,下面接着学习对Zookeeper的相关操作。

二、Zookeeper部署

Zookeeper的部署相对来说还是比较简单,读者可以在网上找到相应的教程,点这里,笔者不再累赘。

Zookeeper有三种运行形式:集群模式、单机模式、伪集群模式。

以下实验都是在单机模式下进行。

三、服务端

bin目录下常用的脚本解释

zkCleanup  清理Zookeeper历史数据,包括事务日志文件和快照数据文件

zkCli     Zookeeper的一个简易客户端

zkEnv    设置Zookeeper的环境变量

zkServer   Zookeeper服务器的启动、停止、和重启脚本

客户端是开发人员使用Zookeeper的主要的途径,以下内容将对Zookeeper的内部原理进行详细的学习和讲解。ZooKeeper的客户端主要有一下几个核心组件组成:

Zookeeper:提供客户端访问ZooKeeper服务器的API.

ClientWatchManager:负责管理客户端注册的Watcher.

HostProvider:客户端地址列表管理器。

ClientCnxn:客户端核心线程,其内部包含连个线程及SendThread和EvnentThread。SendThread是一个IO线程主要负责客户端和服务端之间的网络通信;后者是一个事件处理线程,主要负责对服务端时间进行处理。

Zookeeper的Client直接与用户打交道,是我们使用Zookeeper的interface。了解ZKClient的结构和工作原理有利于我们合理的使用ZK,并能在使用中更早的发现问题。本文将在研究源码的技术上讲述ZK Client的工作原理及内部工作机制。在看完ZK Client的大致架构以后我希望能有一种简单的方式描述ZKClient的基本结构,想来想去我觉得还是图片比较能反映情况,于是我画了这张大致的结构图(来自网络):

 

无标题

模块:

我们可以认为ZK的Client由三个主要模块组成:Zookeeper, WatcherManager, ClientCnxn

Zookeeper是ZK Client端的真正接口,用户可以操作的最主要的类,当用户创建一个Zookeeper实例以后,几乎所有的操作都被这个实例包办了,用户不用关心怎么连接到Server,Watcher什么时候被触发等等令人伤神的问题。WatcherManager,顾名思义,它是用来管理Watcher的,Watcher是ZK的一大特色功能,允许多个Client对一个或多个ZNode进行监控,当ZNode有变化时能够通知到监控这个ZNode的各个Client。我们把一个ZK Client简单看成一个Zookeeper实例,那么这个实例内部的WatcherManager就管理了ZK Client绑定的所有Watcher。ClientCnxn是管理所有网络IO的模块,所有和ZK Server交互的信息和数据都经过这个模块,包括给ZK Server发送Request,从ZK Server接受Response,以及从ZK Server接受Watcher Event。ClientCnxn完全管理了网络,从外部看来网络操作是透明的。

线程:每当我们创建一个Zookeeper实例的时候,会有两个线程被创建:SendThread和EventThread。所以当我们使用ZK Client端的时候应该尽量只创建一个Zookeeper实例并反复使用。大量的创建销毁Zookeeper实例不仅会反复的创建和销毁线程,而且会在Server端创建大量的Session。SendThread是真正处理网络IO的线程,所有通过网络发送和接受的数据包都在这个线程中处理。这个线程的主体是一个while循环:

while (zooKeeper.state.isAlive()) {

try {

if (sockKey == null) {

// don’t re-establish connection if we are closing

if (closing) {

break;

}

startConnect();

lastSend = now;

lastHeard = now;

}

… ….

selector.select(to);

Set<SelectionKey> selected;

synchronized (this) {

selected = selector.selectedKeys();

}

// Everything below and until we get back to the select is

// non blocking, so time is effectively a constant. That is

// Why we just have to do this once, here

now = System.currentTimeMillis();

for (SelectionKey k : selected) {

… …

if (doIO()) {

lastHeard = now;

}

… …

}

}

catch() {

… …

}

}

这里用了java的nio功能,当selector侦测到事件发生的时候就会触发一次循环,主要的操作会在doIO()里面完成:

boolean doIO() throws InterruptedException, IOException {

boolean packetReceived = false;

SocketChannel sock = (SocketChannel) sockKey.channel();

if (sock == null) {

throw new IOException(“Socket is null!”);

}

if (sockKey.isReadable()) {

… …

}

 

if (sockKey.isWritable()) {

… …

}

 

if (outgoingQueue.isEmpty()) {

disableWrite();

} else {

enableWrite();

}

return packetReceived;

}

这个过程大概是这样的:

  1. 如果有数据可读,则读取数据包,如果数据包是先前发出去的Request的Response,那么这个数据包一定在Pending Queue里面。将它从Pending Queue里面移走,并将此信息添加到Waiting Event Queue 里面,如果数据包是一个Watcher Event,将此信息添加到Waiting Event Queue里面。
  2. 如果OutgoingQueue里面有数据需要发送,则发送数据包并把数据包从Outgoing Queue移至Pending Queue,意思是数据我已经发出去了,但还要等待Server端的回复,所以这个请求现在是Pending 的状态。

另外一个线程EventThread是用来处理Event的。前面提到SendThread从Server收到数据的时候会把一些信息添加到Event Thread里面,比如Finish Event和Watcher Event。EventThread就是专门用来处理这些Event的,收到Finish Event的时候会把相对应的Package置成Finish状态,这样等待结果的Client函数就能得以返回。收到Watcher Event的时候会联系WatcherManager找到相对应的Watcher,从WatcherManager里面移除这个Watcher(因为每个Watcher只会被通知一次) 并回调Watcher的process函数。所以所有Watcher的process函数是运行在EventThread里面的。

保持连接:

到目前为止应该已经大概介绍了ZK Client端的大致结构和处理流程。还剩下一个问题就是当网络出问题时ZK Client是如何处理的。其实这个过程并不复杂,大概是执行以下步骤:

  1. 网络发生故障,网络操作抛出的异常被捕获。
  2. 确认网络操作失败,清除当前与Server相关的网络资源,包括Socket等等。
  3. 在Server列表中逐个尝试链接Server。

这个过程从外界看来是透明的,外界并不会觉察到ZK Client已经悄悄地更换了一个连接的Server。

Zookeeper客户端命令详解

使用zkCLi.sh

./zkCli.sh -timeout 0 -r -server ip:port

./zkCli.sh -timeout 5000 -server 127.0.0.1:2181

-r :即使ZooKeeper服务器集群一半以上的服务器当掉,也给客户端提供读服务

(1)connect命令:连接zk服务端,与close命令配合使用可以连接或者断开zk服务端

connect 127.0.0.1:2181

(2)close命令:close命令用于关闭与服务端的链接。

(3)get命令:get命令用于获取节点的信息,注意节点的路径必须是以/开头的绝对路径。如get /

 

cZxid = 0x0

ctime = Thu Jan 01 08:00:00 CST 1970

mZxid = 0x0

mtime = Thu Jan 01 08:00:00 CST 1970

pZxid = 0x1469

cversion = 3

dataVersion = 0

aclVersion = 0

ephemeralOwner = 0x0

dataLength = 0

numChildren = 4

 

其中:

 

cZxid:节点创建时的zxid

ctime:节点创建时间

mZxid:节点最近一次更新时的zxid

mtime:节点最近一次更新的时间

cversion:子节点数据更新次数

dataVersion:本节点数据更新次数

aclVersion:节点ACL(授权信息)的更新次数

ephemeralOwner:如果该节点为临时节点,ephemeralOwner值表示与该节点绑定的session id. 如果该节点不是临时节点,ephemeralOwner值为0

dataLength:节点数据长度,本例中为hello world的长度

numChildren:子节点个数

(4)stat命令

stat命令用于查看节点的状态信息,如stat /

cZxid = 0x0

ctime = Thu Jan 01 08:00:00 CST 1970

mZxid = 0x0

mtime = Thu Jan 01 08:00:00 CST 1970

pZxid = 0x1469

cversion = 3

dataVersion = 0

aclVersion = 0

ephemeralOwner = 0x0

dataLength = 0

numChildren = 4

该命令的结果参数说明同get命令

(5)set命令,set命令用于设置节点的数据,如:

set /usergrid hellUsergrid

(6)ls命令\ls命令用于获取路径下的节点信息,注意路径为绝对路径,如:ls /storm

[assignments, storms, errors, supervisors, workerbeats]

(7)ls2命令:ls2命令是ls命令的增强版,比ls命令多输出本节点信息,如:ls2 /storm

 

[assignments, storms, errors, supervisors, workerbeats]

cZxid = 0x1469

ctime = Tue Nov 14 11:32:09 CST 2017

mZxid = 0x1469

mtime = Tue Nov 14 11:32:09 CST 2017

pZxid = 0x1470

cversion = 5

dataVersion = 0

aclVersion = 0

ephemeralOwner = 0x0

dataLength = 1

numChildren = 5

(8)listquota命令:listquota命令用于显示配额,如listquota /storm

absolute path is /zookeeper/quota/storm/zookeeper_limits

quota for /storm does not exist.

(9)setquota命令:setquota命令用于设置节点个数以及数据长度的配额,如:

setquota –n 4 /zookeeper/node 设置/zookeeper/node子节点个数最大为4

setquota –b 100 /zookeeper/node 设置/zookeeper/node节点长度最大为100

(10)delquota命令

delquota命令用于删除配额,-n为子节点个数,-b为节点数据长度,如:delquota –n 2

(11)history命令:history用于列出最近的命令历史,可以和redo配合使用。如history

12 – get /usergrid

13 – help

14 – ls /

15 – ls /storm

16 – ls2 /storm

17 – help

18 – listquota /storm

19 – listquota /zookeeper

20 – help

21 – history

22 – history

(12)redo命令:redo命令用于再次执行某个命令,使用方式为redo cmdid 如 redo 20

常与history配合使用

 

create命令

create命令用于创建节点,其中-s为顺序充点,-e临时节点

create /zookeeper/node1″test_create” world:anyone:fdsfds

delete命令

delete命令用于删除节点,如delete /nodeDelete

addauth命令

addauth命令用于节点认证,使用方式:如addauth digest username:password

setAcl命令

setAcl命令用于设置节点Acl

Acl由三部分构成:1为scheme,2为user,3为permission,一般情况下表示为scheme:id:permissions

getAcl命令:获取节点的Acl,如getAcl /node1

scheme和id

world: 它下面只有一个id, 叫anyone, world:anyone代表任何人,zookeeper中对所有人有权限的结点就是属于world:anyone的

auth: 它不需要id, 只要是通过authentication的user都有权限(zookeeper支持通过kerberos来进行authencation, 也支持username/password形式的authentication)

digest: 它对应的id为username:BASE64(SHA1(password)),它需要先通过username:password形式的authentication

ip: 它对应的id为客户机的IP地址,设置的时候可以设置一个ip段,比如ip:192.168.1.0/16, 表示匹配前16个bit的IP段

super: 在这种scheme情况下,对应的id拥有超级权限,可以做任何事情(cdrwa)

permissions

CREATE(c): 创建权限,可以在在当前node下创建child node

DELETE(d): 删除权限,可以删除当前的node

READ(r): 读权限,可以获取当前node的数据,可以list当前node所有的child nodes

WRITE(w): 写权限,可以向当前node写数据

ADMIN(a): 管理权限,可以设置当前node的permission

sync命令

sync命令用于强制同步,由于请求在半数以上的zk server上生效就表示此请求生效,那么就会有一些zk server上的数据是旧的。sync命令就是强制同步所有的更新操作。

printwatchers命令:printWatchers命令用于设置和显示监视状态,值为on或则off

quit命令:退出客户端

转载请注明:比度技术-关注互联网技术的个人博客 » Zookeeper客户端