简介
本文会从两个角度介绍:
- shell
- API 支持JAVA、Scala
http://zookeeper.apache.org/官网
管理者ZooKeeper,我们Hadoop生态圈的有很多节点,光靠一个ZooKeeper肯定管理不过来,所以可以单节点也可以分布式
ZK: distributed coordination 分布式协调服务
通过前面我们Hadoop HA高可用里面也可以知道,只有一个namenode 作为active对外服务,这里就用到了ZK协调
再比如生产上需要监控各个节点上面的状态信息,如果某一个节点下线了或者上线了,这也是完全可以通过ZK来实现的
安装部署
https://blog.csdn.net/greenplum_xiaofan/article/details/99020516#31_Zookeeper_40
版本选择3.5,同CDH配套的
前置要求:Java
解压后重点关注以下两个目录
bin/ 重点关注:zkCli.sh zkServer.sh
con/ 配置文件 zoo.cfg默认是没有的,cp zoo_sample.cfg
zoo.cfg中需要修改以下:
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/tmp/zookeeper // 不要使用/tmp/zookeeper,默认重启后就没有了
clientPort=2181
环境搭建完成后,我们来介绍一下ZK数据模型
1)树形结构: 从/开始 //Linux也是树形结构
2)ZK中每个节点:叫做znode,而且都有唯一的路径标识,从根节点访问znode必然是唯一的
a)每个znode都有自己的版本号
b)如果znode信息发生变化,那么version+1
c)znode数据量不要太大,几K,比如hadoop的jar包就不能放进去,主要存放一些配置信息
d)权限
e)每一个几点可以加一个Watcher监视器,如果一个zk发生变化,其他的也会同步
3)znode有两种类型
a)临时:当前session有效,前面对于zk来说是树形结构,也就是文件夹来有文件夹,但是对于临时的不能有子节点
b)永久:不依赖于session
4)znode四种形式
PERSISTENT 持久化的目录节点
PERSISTENT_SEQUENTIAL 带顺序编号的
EPHEMERAL 临时的
EPHEMERAL_SEQUENTIAL 临时的带顺序编号的
ZK节点数:奇数个(>=3)
选举:leader+flower
ZK集群的写操作,leader负责,会把通知所有节点写入操作
只有收到半数以上节点的成功反馈,才算成功
基本操作
服务端启动
[hadoop@vm01 bin]$ ./zkServer.sh //可以查看命令帮助,以后写脚本也可以参照这个USage
[hadoop@vm01 bin]$ ./zkServer.sh start
//启动后可以看到QuorumPeerMain的进程,表示ZK server端已经起了
查看ZK状态,单节点就是standalone,集群是leader,flower,默认是standalone模式
[hadoop@vm01 bin]$ ./zkServer.sh status
打开ZK客户端
[hadoop@vm01 bin]$ ./zkCli.sh
[zk: localhost:2181(CONNECTED) 11] quit
默认启动连到localhost,默认端口2181,到目录后 help可以查看命令帮助
[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper, kafka]
现在看之前配的ZK目录下:/home/hadoop/tmp/zookeeper目录下:
[zk: localhost:2181(CONNECTED) 0] ls /home/hadoop/tmp/zookeeper
version-2 zookeeper_server.pid
[zk: localhost:2181(CONNECTED) 0] cat zookeeper_server.pid
//这里面放的就是刚刚启动进程id
[zk: localhost:2181(CONNECTED) 0] cd version-2/
我们tree 一下可以查看里面的树形结构
ls2 可以理解为 ls+stat
//看状态信息
//stat显示当前的状态信息
[zk: localhost:2181(CONNECTED) 1] ls2 /
[zookeeper, kafka] #下面是ZK的状态信息
cZxid = 0x0
ctime = Wed Dec 31 16:00:00 PST 1969
mZxid = 0x0
mtime = Wed Dec 31 16:00:00 PST 1969
pZxid = 0x2
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 2
显示当前状态信息
[zk: localhost:2181(CONNECTED) 2] stat /
cZxid = 0x0
ctime = Wed Dec 31 16:00:00 PST 1969
mZxid = 0x0
mtime = Wed Dec 31 16:00:00 PST 1969
pZxid = 0x2
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 2
获取
[zk: localhost:2181(CONNECTED) 3] get /zookeeper
cZxid = 0x0 #zk当前的id
ctime = Wed Dec 31 16:00:00 PST 1969 #创建时间
mZxid = 0x0 #修改后的id
mtime = Wed Dec 31 16:00:00 PST 1969 #修改时间
pZxid = 0x0 #最后更新的子节点id
cversion = -1 #子节点版本号
dataVersion = 0 #数据版本
aclVersion = 0 #权限版本
ephemeralOwner = 0x0 #是否是临时的
dataLength = 0 #数据长度
numChildren = 1 #有几个孩子,有几个子节点
create
[-s] #是否带编号SEQUENTIAL,这个在分布式锁的zookeeper实现里会用到
[-e] #是否是EPHEMERAL临时的
[zk: localhost:2181(CONNECTED) 4] create /ruoze ruoze-data
Created /ruoze
[zk: localhost:2181(CONNECTED) 5]create /ruoze/a/b/c abc
//这样在shell里是不行的,不过API是支持的
[zk: localhost:2181(CONNECTED) 6] ls /
[ruoze, zookeeper, kafka]
[zk: localhost:2181(CONNECTED) 7] get /ruoze
ruoze-data #数据
cZxid = 0xe7
ctime = Thu Aug 15 06:16:21 PDT 2019
mZxid = 0xe7
mtime = Thu Aug 15 06:16:21 PDT 2019
pZxid = 0xe7
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 0
[zk: localhost:2181(CONNECTED) 8] create -e /ruoze/xiaoruoze xiaoruoze
Created /ruoze/xiaoruoze
[zk: localhost:2181(CONNECTED) 9] get /ruoze
ruoze-data
cZxid = 0xe7
ctime = Thu Aug 15 06:16:21 PDT 2019
mZxid = 0xe7
mtime = Thu Aug 15 06:16:21 PDT 2019
pZxid = 0xe8
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 1 #有一个孩子了
[zk: localhost:2181(CONNECTED) 10] get /ruoze/xiaoruoze
xiaoruoze
cZxid = 0xe8
ctime = Thu Aug 15 06:18:09 PDT 2019
mZxid = 0xe8
mtime = Thu Aug 15 06:18:09 PDT 2019
pZxid = 0xe8
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x16c88e4a0770001 #表示是临时节点
dataLength = 9
numChildren = 0
[zk: localhost:2181(CONNECTED) 11] quit
[hadoop@vm01 bin]$ ./zkCli.sh
[zk: localhost:2181(CONNECTED) 0] ls /ruoze
[] #发现没有子节点了,这就是临时的特点,退出就没了
加编号,顺序
分布式锁的一个实现,会借助这个
[zk: localhost:2181(CONNECTED) 1] create -s /ruoze/seq seq
Created /ruoze/seq0000000001
[zk: localhost:2181(CONNECTED) 2] create -s /ruoze/seq seq
Created /ruoze/seq0000000002
[zk: localhost:2181(CONNECTED) 3] create -s /ruoze/seq seq
Created /ruoze/seq0000000003
[zk: localhost:2181(CONNECTED) 4] create -s /ruoze/seq seq
Created /ruoze/seq0000000004
[zk: localhost:2181(CONNECTED) 5] create -s /ruoze/seq seq
Created /ruoze/seq0000000005
[zk: localhost:2181(CONNECTED) 6]
[zk: localhost:2181(CONNECTED) 6] ls /ruoze
[seq0000000005, seq0000000003, seq0000000004, seq0000000001, seq0000000002]
修改数据,查看版本信息
[zk: localhost:2181(CONNECTED) 7] get /ruoze
ruoze-data
cZxid = 0xe7
ctime = Thu Aug 15 06:16:21 PDT 2019
mZxid = 0xe7
mtime = Thu Aug 15 06:16:21 PDT 2019
pZxid = 0xef
cversion = 7
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 5
[zk: localhost:2181(CONNECTED) 8] set /ruoze www.ruozedata.com //当然后面也可以加上版本号
cZxid = 0xe7
ctime = Thu Aug 15 06:16:21 PDT 2019
mZxid = 0xf0
mtime = Thu Aug 15 06:24:30 PDT 2019
pZxid = 0xef
cversion = 7
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 17
numChildren = 5
[zk: localhost:2181(CONNECTED) 9] get /ruoze
www.ruozedata.com
cZxid = 0xe7
ctime = Thu Aug 15 06:16:21 PDT 2019
mZxid = 0xf0
mtime = Thu Aug 15 06:24:30 PDT 2019
pZxid = 0xef
cversion = 7
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 17
numChildren = 5
删除
[zk: localhost:2181(CONNECTED) 11] delete /ruoze/seq0000000001
[zk: localhost:2181(CONNECTED) 12] ls /ruoze
[seq0000000005, seq0000000003, seq0000000004, seq0000000002]
修改seq2的数据,然后删除,带版本的,0的版本是删除不掉的,因为修改之后版本+1,必须用最新的版本号才能删除,set,delete都可以带上版本号,建议带上版本号,相当于加了一个乐观锁
[zk: localhost:2181(CONNECTED) 14] set /ruoze/seq0000000002 seq2
[zk: localhost:2181(CONNECTED) 16] delete /ruoze/seq0000000002 0
version No is not valid : /ruoze/seq0000000002
[zk: localhost:2181(CONNECTED) 17] delete /ruoze/seq0000000002 1
ZK四字命令 Four Letter Word
http://zookeeper.apache.org/doc/current/zookeeperAdmin.html
我们来看几个比较重要的命令
stat : Lists brief details for the server and connected clients.
[hadoop@vm01 bin]$ echo stat |nc localhost 2181
Zookeeper version: 3.4.5-cdh5.7.0--1, built on 03/23/2016 18:31 GMT
Clients:
/0:0:0:0:0:0:0:1:50179[0](queued=0,recved=1,sent=0)
//查看状态信息,关注下connnection的数量以及node count 的数量(集群znode的数量)
Latency min/avg/max: 0/0/11
Received: 229
Sent: 228
Connections: 1 #有多少连接数
Outstanding: 0
Zxid: 0xf7
Mode: standalone
Node count: 146 #节点数量
ruok : Tests if server is running in a non-error state. The server will respond with imok if it is running. Otherwise it will not respond at all. A response of “imok” does not necessarily indicate that the server has joined the quorum, just that the server process is active and bound to the specified client port. Use “stat” for details on state wrt quorum and client connection information.
[hadoop@vm01 bin]$ echo ruok |nc localhost 2181
imok
dump : Lists the outstanding sessions and ephemeral nodes. This only works on the leader.
[hadoop@vm01 bin]$ echo dump |nc localhost 2181
SessionTracker dump:
Session Sets (0):
ephemeral nodes dump:
Sessions with Ephemerals (0): #临时节点有多少个
conf : New in 3.3.0: Print details about serving configuration.
可以看到ZK的配置
[hadoop@vm01 bin]$ echo conf |nc localhost 2181
clientPort=2181
dataDir=/home/hadoop/app/zookeeper-3.4.5-cdh5.7.0/data/version-2
dataLogDir=/home/hadoop/app/zookeeper-3.4.5-cdh5.7.0/dataLog/version-2
tickTime=2000
maxClientCnxns=60
minSessionTimeout=4000
maxSessionTimeout=40000
serverId=0
[hadoop@vm01 bin]$ echo cons |nc localhost 2181
/0:0:0:0:0:0:0:1:50187[0](queued=0,recved=1,sent=0)
//可以查看当前的ZK配置信息
cons : New in 3.3.0: List full connection/session details for all clients connected to this server. Includes information on numbers of packets received/sent, session id, operation latencies, last operation performed, etc…
[hadoop@vm01 bin]$ echo cons |nc llocalhost 2181
//所有链接到server的connection信息
envi : Print details about serving environment
[hadoop@vm01 bin]$ echo envi |nc llocalhost 2181
//打出环境、依赖信息
mntr : New in 3.4.0: Outputs a list of variables that could be used for monitoring the health of the cluster.
监控
[hadoop@vm01 bin]$ echo mntr |nc localhost 2181
zk_version 3.4.5-cdh5.7.0--1, built on 03/23/2016 18:31 GMT
zk_avg_latency 0
zk_max_latency 11
zk_min_latency 0
zk_packets_received 234
zk_packets_sent 233
zk_num_alive_connections 1
zk_outstanding_requests 0
zk_server_state standalone
zk_znode_count 146
zk_watch_count 0
zk_ephemerals_count 0
zk_approximate_data_size 11597
zk_open_file_descriptor_count 28
zk_max_file_descriptor_count 65536
//输出当前正在用的节点健康信息
wchs : New in 3.3.0: Lists brief information on watches for the server.
查看有多少watcher
[zk: localhost:2181(CONNECTED) 1] get /ruoze watch #加一把锁
[hadoop@vm01 bin]$ echo wchs |nc localhost 2181
1 connections watching 1 paths
Total watches:1 #数量1个
// znode都有一个watcher,当监控的某个znode发生了变化,就会触发watcher事件, zk中的watcher是一次性的:API,父节点、子节点的增删改都会被监听
API简单使用
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.slf4j.LoggerFactory;
public class ZooKeeperConnectApp implements Watcher{
private static Logger logger = LoggerFactory.getLogger(ZooKeeperConnectApp.class);
public static void mai(String[]args){
try {
ZooKeeper zk = new ZooKeeper("hadoop001:2181",5000,new ZooKeeperConnectApp);
//connect、sessionout超时时长、watcher
logger.warn("客户端开始连接zk...,状态是:{}" ,zk.getState());//写log4j的时尽量不要使用"+","+"底层会有很大问题,否则的话底层拼一堆SB,另外这边log4j error的日志级别还不够,需要改成info
Thread.sleep(2000); //zk可能由于网络的问题连不上,所以我们用最low的方式
logger.warn("状态是:{}",zk.getState());
}catch(Exception e){
e.printStackTrace();
}
}
public void process(WatchedEvent watchedEvent){
}
}
要想操作首先肯定要先connect上,当然上面的方式太low了,能不能有更好的方式能连上后直接返回,不用等那么久,接着看
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.slf4j.LoggerFactory;
public class ZooKeeperConnectApp implements Watcher{
private static Logger logger = LoggerFactory.getLogger(ZooKeeperConnectApp.class);
private static CountDown connected = new CountDownLatch(1);
//这一步的意思是往下减,初始值是1
public static void main(String[]args){
try {
ZooKeeper zk = new ZooKeeper("hadoop001:2181",5000,new ZooKeeperConnectApp);
//connect、sessionout超时时长、watcher
logger.warn("客户端开始连接zk...,状态是:{}" ,zk.getState());//写log4j的时尽量不要使用"+","+"底层会有很大问题,否则的话底层拼一堆SB,另外这边log4j error的日志级别还不够,需要改成info
//Thread.sleep(2000); //zk可能由于网络的问题连不上,所以我们用最low的方式
connected.await();//等到countdown执行完才会往下走
logger.warn("状态是:{}",zk.getState());
}catch(Exception e){
e.printStackTrace();
}
}
public void process(WatchedEvent watchedEvent){
//这里连接状态一致
if (Event.KeeperState.SyncConnected == watchedEvent.getState()){
logger.warn("接收到WAtch通知:{}",watchedEvent);
connected.countDown(); //1-1=0
}
}
}
上面这种方式比较优雅,这是Java并发里面的