Zookeeper
Zookeeper入门
概述
Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目。
Zookeeper从设计模式角度来理解,是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生了变化,Zookeeper就负责通知已经在Zookeeper上注册的那些观察者做出相应的反应.
Zookeeper = 文件系统 + 通知机制
Zookpper工作机制
特点
半数机制 (搭建集群 奇数台机制)
数据结构
应用场景
提供的服务包括:统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、软负载均衡等。
统一命名服务
统一配置管理
统一集群管理
服务器节点动态上下线
软负载均衡
下载地址
https://zookeeper.apache.org/
Zookeeper安装
本地模式安装部署
安装前准备:
(1)安装Jdk
(2)拷贝Zookeeper安装包到Linux系统下
(3)解压到指定目录
[vanas@hadoop130 software]$ rz
[vanas@hadoop130 software]$ ll
总用量 351924
-r--------. 1 vanas vanas 9311744 4月 20 17:50 apache-zookeeper-3.5.7-bin.tar.gz
[vanas@hadoop130 software]$ tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz -C ../module/
[vanas@hadoop130 software]$ cd /opt/module/
[vanas@hadoop130 module]$ mv apache-zookeeper-3.5.7-bin zookeeper-3.5.7
配置修改
(1)将/opt/module/zookeeper-3.5.7/conf这个路径下的zoo_sample.cfg修改为zoo.cfg;
[vanas@hadoop130 conf]$ cp zoo_sample.cfg zoo.cfg
(2)打开zoo.cfg文件,修改dataDir路径:
[vanas@hadoop130 conf]$ vim zoo.cfg
dataDir=/opt/module/zookeeper-3.5.7/zkData
(3)在/opt/module/zookeeper-3.5.7/这个目录上创建zkData文件夹
[vanas@hadoop130-3.5.7]$ mkdir zkData
[vanas@hadoop130 zkData]$ vim myid
操作
(1)启动Zookeeper
[vanas@hadoop130 zookeeper-3.5.7]$ bin/zkServer.sh start
(2)查看进程是否启动
[vanas@hadoop130 zookeeper-3.5.7]$ jps
4020 Jps
4001 QuorumPeerMain
(3)查看状态:
[vanas@hadoop130 zookeeper-3.5.7]$ bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Mode: standalone
(4)启动客户端:
[vanas@hadoop130 zookeeper-3.5.7]$ bin/zkCli.sh
(5)退出客户端:
[zk: localhost:2181(CONNECTED) 0] quit
(6)停止Zookeeper
[vanas@hadoop130 zookeeper-3.5.7]$ bin/zkServer.sh stop
配置参数解读
Zookeeper中的配置文件zoo.cfg中参数含义解读如下:
1)tickTime =2000:通信心跳数,Zookeeper服务器与客户端心跳时间,单位毫秒
Zookeeper使用的基本时间,服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个tickTime时间就会发送一个心跳,时间单位为毫秒。
它用于心跳机制,并且设置最小的session超时时间为两倍心跳时间。(session的最小超时时间是2*tickTime)
2)initLimit =10:LF初始通信时限
集群中的Follower跟随者服务器与Leader领导者服务器之间初始连接时能容忍的最多心跳数(tickTime的数量),用它来限定集群中的Zookeeper服务器连接到Leader的时限。
3)syncLimit =5:LF同步通信时限
集群中Leader与Follower之间的最大响应时间单位,假如响应超过syncLimit * tickTime,Leader认为Follwer死掉,从服务器列表中删除Follwer。
4)dataDir:数据文件目录+数据持久化路径
主要用于保存Zookeeper中的数据。
5)clientPort =2181:客户端连接端口
监听客户端连接的端口。
Zookeeper的四字命令[了解]
跟据开发环境进行配置,存在安全问题。
Zookeeper支持某些特定的四字命令(The Four Letter Words) 与其进行交互,它们大多是查询命令,用来获取Zookeeper服务的当前状态及相关信息,用户在客户端可以通过telnet或nc 向Zookeeper提交相应的命令。
需要在Zookeeper的配置文件中加入如下配置:
Zoo.cfg
4lw.commands.whitelist=*
Zookeeper常用四字命令主要如下:
ruok | 测试服务是否处于正确状态,如果确实如此,那么服务返回 imok ,否则不做任何响应。 |
---|---|
conf | 3.3.0版本引入的,打印出服务相关配置的详细信息 |
cons | 列出所有连接到这台服务器的客户端全部会话详细信息。包括 接收/发送的包数量,会话id,操作延迟、最后的操作执行等等信息 |
crst | 重置所有连接的连接和会话统计信息 |
dump | 列出那些比较重要的会话和临时节点。这个命令只能在leader节点上有用 |
envi | 打印出服务环境的详细信息 |
[vanas@hadoop130 zookeeper-3.5.7]$ nc hadoop130 2181
ruok
imok
Zookeeper内部原理
节点类型
[zk: localhost:2181(CONNECTED) 1] create /vanas/bigdata/love "love"
Created /vanas/bigdata/love
[zk: localhost:2181(CONNECTED) 2] ls /vanas/bigdata
[haha0000000002, love, xixi0000000001]
[zk: localhost:2181(CONNECTED) 3] create -s /vanas/bigdata/me
Created /vanas/bigdata/me0000000004
[zk: localhost:2181(CONNECTED) 4] delete /vanas/bigdata/xixi0000000001
[zk: localhost:2181(CONNECTED) 5] ls /vanas/bigdata
[haha0000000002, love, me0000000004
Stat结构体
(1)czxid-创建节点的事务zxid
每次修改ZooKeeper状态都会收到一个zxid形式的时间戳,也就是ZooKeeper事务ID。
事务ID是ZooKeeper中所有修改总的次序。每个修改都有唯一的zxid,如果zxid1小于zxid2,那么zxid1在zxid2之前发生。
(2)ctime - znode被创建的毫秒数(从1970年开始)
(3)mzxid - znode最后更新的事务zxid
(4)mtime - znode最后修改的毫秒数(从1970年开始)
(5)pZxid-znode最后更新的子节点zxid
(6)cversion - znode子节点变化号,znode子节点修改次数
(7)dataversion - znode数据变化号
(8)aclVersion - znode访问控制列表的变化号
(9)ephemeralOwner- 如果是短暂节点,这个是znode拥有者的session id。如果不是临时节点则是0。
(10)dataLength- znode的数据长度
(11)numChildren - znode子节点数量
[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 1] stat /zookeeper
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -2
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 2
监听器原理(面试重点)
Paxos算法(扩展)
Paxos算法一种基于消息传递且具有高度容错特性的一致性算法。
分布式系统中的节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing)。基于消息传递通信模型的分布式系统,不可避免的会发生以下错误:进程可能会慢、被杀死或者重启,消息可能会延迟、丢失、重复,在基础 Paxos 场景中,先不考虑可能出现消息篡改即拜占庭错误的情况。Paxos 算法解决的问题是在一个可能发生上述异常的分布式系统中如何就某个值达成一致,保证不论发生以上任何异常,都不会破坏决议的一致性。
Paxos算法流程中的每条消息描述如下:
(1)Prepare: Proposer生成全局唯一且递增的Proposal ID (可使用时间戳加Server ID),向所有Acceptors发送Prepare请求,这里无需携带提案内容,只携带Proposal ID即可。
(2)Promise: Acceptors收到Prepare请求后,做出“两个承诺,一个应答”。
两个承诺:
不再接受Proposal ID小于等于(注意:这里是<= )当前请求的Prepare请求。
不再接受Proposal ID小于(注意:这里是< )当前请求的Propose请求。
一个应答:
不违背以前做出的承诺下,回复已经Accept过的提案中Proposal ID最大的那个提案的Value和Proposal ID,没有则返回空值。
(3)Propose: Proposer 收到多数Acceptors的Promise应答后,从应答中选择Proposal ID最大的提案的Value,作为本次要发起的提案。如果所有应答的提案Value均为空值,则可以自己随意决定提案Value。然后携带当前Proposal ID,向所有Acceptors发送Propose请求。
(4)Accept: Acceptor收到Propose请求后,在不违背自己之前做出的承诺下,接受并持久化当前Proposal ID和提案Value。
(5)Learn: Proposer收到多数Acceptors的Accept后,决议形成,将形成的决议发送给所有Learners。
下面我们针对上述描述做三种情况的推演举例:为了简化流程,我们这里不设置Learner。
造成这种情况的原因是系统中有一个以上的Proposer,多个Proposers相互争夺Acceptors,造成迟迟无法达成一致的情况。针对这种情况,一种改进的Paxos算法被提出:从系统中选出一个节点作为Leader,只有Leader能够发起提案。这样,一次Paxos流程中只有一个Proposer,不会出现活锁的情况,此时只会出现例子中第一种情况。
选举机制(面试重点)
(1)半数机制:集群中半数以上机器存活,集群可用。所以Zookeeper适合安装奇数台服务器。
(2)Zookeeper虽然在配置文件中并没有指定Master和Slave。但是,Zookeeper工作时,是有一个节点为Leader,其他则为Follower,Leader是通过内部的选举机制临时产生的。
(3)以一个简单的例子来说明整个选举的过程。
假设有五台服务器组成的Zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的。假设这些服务器依序启动,来看看会发生什么。
第一种情况,没有数据
(1)服务器1启动,发起一次选举。服务器1投自己一票。此时服务器1票数一票,不够半数以上(3票),选举无法完成,服务器1状态保持为LOOKING;
(2)服务器2启动,再发起一次选举。服务器1和2分别投自己一票并交换选票信息:此时服务器1发现服务器2的ID比自己目前投票推举的(服务器1)大,更改选票为推举服务器2。此时服务器1票数0票,服务器2票数2票,没有半数以上结果,选举无法完成,服务器1,2状态保持LOOKING
(3)服务器3启动,发起一次选举。此时服务器1和2都会更改选票为服务器3。此次投票结果:服务器1为0票,服务器2为0票,服务器3为3票。此时服务器3的票数已经超过半数,服务器3当选Leader。服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;
(4)服务器4启动,发起一次选举。此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。交换选票信息结果:服务器3为3票,服务器4为1票。此时服务器4服从多数,更改选票信息为服务器3,并更改状态为FOLLOWING;
(5)服务器5启动,同4一样当小弟。
第二种情况:集群中有数据,选举时要考虑数据的完整性,优先对比zxid
一般情况下zxid都相同
假设leader故障,重新发起选举
选举过程:
第一种情况: 集群是新搭建好的,没有存储任何数据.
假设有5台机器, 启动顺序为 1 2 3 4 5 , 每台机器都有自己的id, 假设id为: 1-1 2-2 3-3 4-4 5-5
启动机器1: 发起投票,投票格式为(zxid,机器id) , 投出的票为(0,1) , 选举未成功
启动机器2: 发起投票, 投出的票为(0,2) ,机器2收到机器1的投票,(0,2) 和 (0,1) 对比, (0,2)胜出,
机器2保持原有投票. 机器1收到机器2的投票, 比较(0,1) 和(0,2)对比,(0,2)胜出,
机器1改变投票(0,2) , 选举未成功
启动机器3: 发起投票,投出的票为(0,3), 机器3收到机器1 和机器2的投票(0,2) , 对比后,不改变投票,
机器1 和机器2 收到 机器3的投票,对比后,改变投票, 投出(0,3) ,
此刻, 机器数已经大于半数,因此leader选举成功, 机器3成为leader. 机器1 和机器2 成为follower
启动机器4: 因已选举出leader, 机器4作为follower
启动机器5: 因已选举出leader, 机器5作为follower
第二种情况: 集群中有数据, 选举时要优先考虑数据的完整性,优先比对zxid
假设leader故障,重新发起选举
机器1: 投票(3,1)
机器2: 投票(2,2)
机器3: 故障, 原来的leader
机器4: 投票(2,4)
机器5: 投票(2,5)
写数据流程
Zookeeper实战(开发重点)
分布式安装部署
1)集群规划
在hadoop102、hadoop103和hadoop104三个节点上部署Zookeeper。
2)解压安装
(1)在hadoop102解压Zookeeper安装包到/opt/module/目录下
[vanas@hadoop130 software]$ tar -zxvf zookeeper-3.5.7.tar.gz -C /opt/module/
3)配置服务器编号
(1)在/opt/module/zookeeper-3.5.7/这个目录下创建zkData
[vanas@hadoop130zookeeper-3.5.7]$ mkdir -p zkData
(2)在/opt/module/zookeeper-3.5.7/zkData目录下创建一个myid的文件
[vanas@hadoop130 zkData]$ touch myid
添加myid文件,注意一定要在linux里面创建
(3)编辑myid文件
[vanas@hadoop130 zkData]$ vi myid
2
在文件中添加与server对应的编号:2
(4)拷贝配置好的zookeeper到其他机器上
[vanas@hadoop130 module ]$ xsync zookeeper-3.5.7
并分别在hadoop133、hadoop134上修改myid文件中内容为3、4
4)配置zoo.cfg文件
(1)重命名/opt/module/zookeeper-3.5.7/conf这个目录下的zoo_sample.cfg为zoo.cfg
[vanas@hadoop130 conf]$ mv zoo_sample.cfg zoo.cfg
(2)打开zoo.cfg文件
[vanas@hadoop130 conf]$ vim zoo.cfg
修改数据存储路径配置
dataDir=/opt/module/zookeeper-3.5.7/zkData
增加如下配置
#######################cluster##########################
server.2=hadoop130:2888:3888
server.3=hadoop133:2888:3888
server.4=hadoop134:2888:3888
(3)同步zoo.cfg配置文件
[vanas@hadoop130102 conf]$ xsync zoo.cfg
(4)配置参数解读
server.A=B:C:D。
A是一个数字,表示这个是第几号服务器;
集群模式下配置一个文件myid,这个文件在dataDir目录下,这个文件里面有一个数据就是A的值,Zookeeper启动时读取此文件,拿到里面的数据与zoo.cfg里面的配置信息比较从而判断到底是哪个server。
B是这个服务器的地址;
C是这个服务器Follower与集群中的Leader服务器交换信息的端口;
D是万一集群中的Leader服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口。
5)集群操作
(1)分别启动Zookeeper
[vanas@hadoop130 zookeeper-3.5.7]$ bin/zkServer.sh start
[vanas@hadoop133 zookeeper-3.5.7]$ bin/zkServer.sh start
[vanas@hadoop134 zookeeper-3.5.7]$ bin/zkServer.sh start
(2)查看状态
[vanas@hadoop130 zookeeper-3.5.7]# bin/zkServer.sh status
JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Mode: follower
[vanas@hadoop133 zookeeper-3.5.7]# bin/zkServer.sh status
JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Mode: leader
[vanas@hadoop134 zookeeper-3.4.5]# bin/zkServer.sh status
JMX enabled by default
Using config: /opt/module/zookeeper-3.5.7/bin/../conf/zoo.cfg
Mode: follower
群起
/home/vanas/bin/zk
#!/bin/bash
if [ $# -lt 1 ]
then
echo " no args input.."
exit;
fi
for i in hadoop130 hadoop133 hadoop134
do
case $1 in
"start")
echo "================== $i $1 ZOOKEEPER ======================="
ssh $i /opt/module/zookeeper-3.5.7/bin/zkServer.sh start
;;
"status")
echo "================== $i $1 ZOOKEEPER ======================="
ssh $i /opt/module/zookeeper-3.5.7/bin/zkServer.sh status
;;
"stop")
echo "================== $i $1 ZOOKEEPER ======================="
ssh $i /opt/module/zookeeper-3.5.7/bin/zkServer.sh stop
;;
*)
echo "input args error.."
;;
esac
done
zk 启停脚本编写:
解决ssh到远程机器找不到JAVA_HOME环境变量问题,可以在每台机器的atguigu的家目录下的.bashrc中配置JAVA_HOME
例如:
在.bashrc文件中加入: export JAVA_HOME=/opt/module/jdk1.8.0_212
客户端命令行操作
命令基本语法 | 功能描述 |
---|---|
help | 显示所有操作命令 |
ls path | 使用 ls 命令来查看当前znode的子节点 [可监听]-w 监听子节点变化-s 附加次级信息 |
create | 普通创建-s 含有序列-e 临时(重启或者超时消失) |
get path | 获得节点的值 [可监听]-w 监听节点内容变化-s 附加次级信息 |
set | 设置节点的具体值 |
stat | 查看节点状态 |
delete | 删除节点 |
deleteall | 递归删除节点 |
1)启动客户端
[vanas@hadoop130 zookeeper-3.5.7]$ bin/zkCli.sh
2)显示所有操作命令
[zk: localhost:2181(CONNECTED) 1] help
3)查看当前znode中所包含的内容
[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]
4)查看当前节点详细数据 ls -s
[zk: localhost:2181(CONNECTED) 1] ls2 /
'ls2' has been deprecated. Please use 'ls [-s] path' instead.
[zookeeper]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
5)分别创建2个普通节点
[zk: localhost:2181(CONNECTED) 3] create /sanguo "jinlian"
Created /sanguo
[zk: localhost:2181(CONNECTED) 4] create /sanguo/shuguo "liubei"
Created /sanguo/shuguo
6)获得节点的值
[zk: localhost:2181(CONNECTED) 5] get /sanguo
jinlian
cZxid = 0x100000003
ctime = Wed Aug 29 00:03:23 CST 2018
mZxid = 0x100000003
mtime = Wed Aug 29 00:03:23 CST 2018
pZxid = 0x100000004
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 1
[zk: localhost:2181(CONNECTED) 6]
[zk: localhost:2181(CONNECTED) 6] get /sanguo/shuguo
liubei
cZxid = 0x100000004
ctime = Wed Aug 29 00:04:35 CST 2018
mZxid = 0x100000004
mtime = Wed Aug 29 00:04:35 CST 2018
pZxid = 0x100000004
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
7)创建短暂节点
[zk: localhost:2181(CONNECTED) 7] create -e /sanguo/wuguo "zhouyu"
Created /sanguo/wuguo
(1)在当前客户端是能查看到的
[zk: localhost:2181(CONNECTED) 3] ls /sanguo
[wuguo, shuguo]
(2)退出当前客户端然后再重启客户端
[zk: localhost:2181(CONNECTED) 12] quit
[vanas@hadoop134 zookeeper-3.5.7]$ bin/zkCli.sh
(3)再次查看根目录下短暂节点已经删除
[zk: localhost:2181(CONNECTED) 0] ls /sanguo
[shuguo]
8)创建带序号的节点
(1)先创建一个普通的根节点/sanguo/weiguo
[zk: localhost:2181(CONNECTED) 1] create /sanguo/weiguo "caocao"
Created /sanguo/weiguo
(2)创建带序号的节点
[zk: localhost:2181(CONNECTED) 2] create -s /sanguo/weiguo/xiaoqiao "jinlian"
Created /sanguo/weiguo/xiaoqiao0000000000
[zk: localhost:2181(CONNECTED) 3] create -s /sanguo/weiguo/daqiao "jinlian"
Created /sanguo/weiguo/daqiao0000000001
[zk: localhost:2181(CONNECTED) 4] create -s /sanguo/weiguo/diaocan "jinlian"
Created /sanguo/weiguo/diaocan0000000002
如果原来没有序号节点,序号从0开始依次递增。如果原节点下已有2个节点,则再排序时从2开始,以此类推。
9)修改节点数据值
[zk: localhost:2181(CONNECTED) 6] set /sanguo/weiguo "simayi"
10)节点的值变化监听
(1)在hadoop104主机上注册监听/sanguo节点数据变化
[zk: localhost:2181(CONNECTED) 26] [zk: localhost:2181(CONNECTED) 8] get -w /sanguo
(2)在hadoop103主机上修改/sanguo节点的数据
[zk: localhost:2181(CONNECTED) 1] set /sanguo "xisi"
(3)观察hadoop104主机收到数据变化的监听
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/sanguo
11)节点的子节点变化监听(路径变化)
(1)在hadoop104主机上注册监听/sanguo节点的子节点变化
[zk: localhost:2181(CONNECTED) 1] ls -w /sanguo
[aa0000000001, server101]
(2)在hadoop103主机/sanguo节点上创建子节点
[zk: localhost:2181(CONNECTED) 2] create /sanguo/jin "simayi"
Created /sanguo/jin
(3)观察hadoop104主机收到子节点变化的监听
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/sanguo
12)删除节点
[zk: localhost:2181(CONNECTED) 4] delete /sanguo/jin
13)递归删除节点
[zk: localhost:2181(CONNECTED) 15] deleteall /sanguo/shuguo
14)查看节点状态
[zk: localhost:2181(CONNECTED) 17] stat /sanguo
cZxid = 0x100000003
ctime = Wed Aug 29 00:03:23 CST 2018
mZxid = 0x100000011
mtime = Wed Aug 29 00:21:23 CST 2018
pZxid = 0x100000014
cversion = 9
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 1
API应用
IDEA环境搭建
1)创建一个Maven Module
2)添加pom文件
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.7</version>
</dependency>
</dependencies>
3)拷贝log4j.properties文件到项目根目录
需要在项目的src/main/resources目录下,新建一个文件,命名为“log4j.properties”,在文件中填入。
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
4)各种api测试
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
/**
* @author Vanas
* @create 2020-04-21 3:45 下午
*/
public class TestZookeeper {
private String connectionString = "hadoop130:2181,hadoop133:2181,hadoop134:2181";
private int sessionTimeOut = 10000;
private ZooKeeper zkClient;
/**
* 删除
*/
@Test
public void testDeleteNode() throws KeeperException, InterruptedException {
Stat stat = zkClient.exists("/vanas/java", false);
if (stat == null) {
System.out.println("删除的节点不存在");
} else {
zkClient.delete("/vanas/java", stat.getVersion());
}
}
/**
* 获取节点内容,设置节点内容
*/
@Test
public void testNodeData() throws KeeperException, InterruptedException {
Stat stat = zkClient.exists("/vanas", false);
if (stat == null) {
System.out.println("不存在");
} else {
byte[] data = zkClient.getData("/vanas", false, stat);
System.out.println("data:" + new String(data));
}
// 如果版本为-1 ,则忽略版本的限制
zkClient.setData("/vanas", "vanaswang".getBytes(), stat.getVersion());//乐观锁
}
/**
* 获取子节点
*
* @throws IOException
*/
@Test
public void testChildNode() throws KeeperException, InterruptedException {
// List<String> children = zkClient.getChildren("/", false);
// 监听只管一次
List<String> children = zkClient.getChildren("/", new Watcher() {
public void process(WatchedEvent event) {
System.out.println("子节点发生变化");
// 重新获取新的子节点
}
});
for (String child : children) {
System.out.println(child);
}
// 让线程不结束
Thread.sleep(Long.MAX_VALUE);
}
// 创建节点
@Test
public void testCreateNode() throws KeeperException, InterruptedException {
String s = zkClient.create("/vanas/html", "Java Is Good".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
System.out.println("s:" + s);
}
@Before
public void before() throws IOException {
zkClient = new ZooKeeper(connectionString, sessionTimeOut, new Watcher() {
public void process(WatchedEvent event) {
}
});
}
@After
public void after() throws InterruptedException {
zkClient.close();
}
@Test
public void testZKClient() throws IOException, InterruptedException {
// 获取客户端连接对象
String connectionString = "hadoop130:2181,hadoop133:2181,hadoop134:2181";
// minSessionTimeout=4000
// maxSessionTimeout=40000
int sessionTimeOut = 10000;
ZooKeeper zkClient = new ZooKeeper(connectionString, sessionTimeOut, new Watcher() {
// 回调方法,当watch对象监听到感兴趣的事件后,会调用process方法
// 在process方法中作出相应处理
// watcheEvent 事件对象,封装了所发生的事
public void process(WatchedEvent event) {
//
}
});
// 操作
System.out.println("zkClient" + zkClient);
// 关闭
zkClient.close();
}
}
悲观锁 和 乐观锁:
什么时候用锁? 安全
悲观锁: 总有刁民想害朕. 例如: 在数据库中操作数据时, 读数据也会加锁. 效率比较低.
乐观锁: 世界上都是好人. 例如: 在数据库中操作数据时, 读写数据不会加锁. 效率高.
通过数据的版本来实现数据的安全.
xxx表
1001 zhangsan 20 10000 0(版本号)
1002 lisi 30 20000 1(版本号)
假设两个线程并发 修改 1002这条数据:
线程A: 先读取1002数据, 得到版本号为1 , 将20000 --> 15000 , 版本号变为2
线程B: 先读取1002数据, 得到版本号为1 , 将20000 --> 25000 , 线程B手中的版本为1,但数据库中当前的版本为2,则修改失败
监听服务器节点动态上下线案例(扩展)
1)需求
某分布式系统中,主节点可以有多台,可以动态上下线,任意一台客户端都能实时感知到主节点服务器的上下线。
2)需求分析
Server
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
/**
* 服务器上线后 将服务器对应信息写入zk中
*
* @author Vanas
* @create 2020-04-22 9:13 上午
*/
public class Server {
private String connectionsString = "hadoop130:2181,hadoop133:2181,hadoop134:2181";
private int sessionTimeOut = 10000;
private ZooKeeper zkClient = null;
private String parentNode = "/server";
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
Server server = new Server();
// 初始化zk客户端对象
server.init();
// 判断zk中存储服务器信息的znode是否存在
server.parentNodeExits();
// 将服务器信息写入到zk中
server.writeServer(args);
// 保持线程不结束
Thread.sleep(Long.MAX_VALUE);
}
private void writeServer(String[] args) throws KeeperException, InterruptedException {
String s = zkClient.create(parentNode + "/" + args[0], args[1].getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL);
System.out.println("***********" + s + "is online ******************");
}
private void parentNodeExits() throws KeeperException, InterruptedException {
Stat stat = zkClient.exists(parentNode, false);
if (stat == null) {
// 创建节点
zkClient.create(parentNode, "servers".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
private void init() throws IOException {
zkClient = new ZooKeeper(connectionsString, sessionTimeOut, new Watcher() {
public void process(WatchedEvent event) {
}
});
}
}
Client
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.util.List;
/**
* 时刻监听zk中服务器信息,只要发生改变,就读取取数据
*
* @author Vanas
* @create 2020-04-22 9:13 上午
*/
public class Client {
private String connectionString = "hadoop130:2181,hadoop133:2181,hadoop134:2181";
private int sessionTimeOut = 10000;
private ZooKeeper zkClient;
private String parentNode = "/server"; //约定 /server在zk中一定存在
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
Client client = new Client();
// 1.初始化zk客户端对象
client.init();
// 2.获取服务器的信息
client.readServers();
// 3.保持线程不结束
Thread.sleep(Long.MAX_VALUE);
}
private void readServers() throws KeeperException, InterruptedException {
List<String> children = zkClient.getChildren(parentNode, new Watcher() {
public void process(WatchedEvent event) {
// 当在线的服务器放生变化后,会执行process方法,我们需要重新获取最新的服务器信息
try {
readServers();
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 当前在线的服务器信息打印到控制台
System.out.println("Current Online Servers: " + children);
}
private void init() throws IOException {
zkClient = new ZooKeeper(connectionString, sessionTimeOut, new Watcher() {
public void process(WatchedEvent event) {
}
});
}
}
企业面试真题
5.1 请简述ZooKeeper的选举机制
5.2 ZooKeeper的监听原理是什么?
5.3 ZooKeeper的部署方式有哪几种?集群中的角色有哪些?集群最少需要几台机器?
(1)部署方式单机模式、集群模式
(2)角色:Leader和Follower
(3)集群最少需要机器数:3
5.4 ZooKeeper的常用命令
ls create get delete set…