网站建设的三网合一,瑞金网络推广,比较有创意的广告,做十来个网站优化ZooKeeper学习1
工作机制 数据结构 使用场景
统一配置管理 统一集群管理 软负载均衡 服务器动态上下线 统一命名服务
本地安装ZooKeeper
安装在Linux虚拟机上#xff0c;系统我用Cent OS 7版本。要先安装Java#xff0c;再安装ZooKeeper。我虚拟机里之前已经装了Jav…ZooKeeper学习1
工作机制 数据结构 使用场景
统一配置管理 统一集群管理 软负载均衡 服务器动态上下线 统一命名服务
本地安装ZooKeeper
安装在Linux虚拟机上系统我用Cent OS 7版本。要先安装Java再安装ZooKeeper。我虚拟机里之前已经装了Java这里就直接开始安装ZooKeeperZooKeeper的版本是3.5.7。 1.去官网下载apache-zookeeper-3.5.7-bin.tar.gz主要是名字里有bin的。 2.在虚拟机里在/opt文件夹下创建software文件夹和module文件夹 3.把apache-zookeeper-3.5.7-bin.tar.gz放到这个/opt/software目录下再解压缩
tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz -C /opt/module/这里指定了解压到/opt/module/这个文件夹下到/opt/module/打开apache-zookeeper-3.5.7-bin文件夹
[rootvmcent apache-zookeeper-3.5.7-bin]# ll
总用量 32
drwxr-xr-x. 2 502 games 232 2月 10 2020 bin
drwxr-xr-x. 2 502 games 77 2月 7 2020 conf
drwxr-xr-x. 5 502 games 4096 2月 10 2020 docs
drwxr-xr-x. 2 root root 4096 9月 1 11:46 lib
-rw-r--r--. 1 502 games 11358 9月 13 2018 LICENSE.txt
-rw-r--r--. 1 502 games 432 2月 10 2020 NOTICE.txt
-rw-r--r--. 1 502 games 1560 2月 7 2020 README.md
-rw-r--r--. 1 502 games 1347 2月 7 2020 README_packaging.txt在这里创建一个文件夹zkData
mkdir ./zkData[rootvmcent apache-zookeeper-3.5.7-bin]# cd ./zkData
[rootvmcent zkData]# pwd
/opt/module/apache-zookeeper-3.5.7-bin/zkData回到apache-zookeeper-3.5.7-bin打开这里面的conf文件夹把zoo_sample.cfg文件名改为zoo.cfg
mv zoo_sample.cfg zoo.cfg打开这个zoo.cfg
vim zoo.cfg把这里的dataDir的值修改为/opt/module/apache-zookeeper-3.5.7-bin/zkData保存退出。 进入/apache-zookeeper-3.5.7-bin/bin文件夹里 应该先启动Server再启动client。 首先启动Server
./zkServer.sh start再启动client
./zkCli.sh启动后简单试一下命令
[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]退出客户端命令
quit查看ZooKeeper状态./zkServer.sh status
[rootvmcent bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: standalonestandalone表示独立的。 停止Zookeeper服务./zkServer.sh stop
[rootvmcent bin]# ./zkServer.sh stop
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Stopping zookeeper ... STOPPED配置文件zoo.cfg中参数含义 1.tickTime 2000通信心跳时间Zookeeper服务器与客户端心跳时间单位毫秒 2.initLimit 10LF初始通信时限 Leader和Follower初始连接时能容忍的最多心跳数tickTime的数量 3.syncLimit 5LF同步通信时限 Leader和Follower之间通信时间如果超过syncLimit * tickTimeLeader认为Follwer死掉从服务器列表中删除Follwer。 4.dataDir保存Zookeeper中的数据 注意默认的tmp目录容易被Linux系统定期删除所以一般不用默认的tmp目录。 5.clientPort 2181客户端连接端口通常不做修改。
集群方式安装
为了模拟集群方式安装开启多台虚拟机来模拟集群的方式。这里我就用3台虚拟机来模拟。 3台虚拟机都要有安装JDK和ZooKeeper因为这里是虚拟机嘛其余2台都是从第一台克隆出来的这样就不用再安装了为了好区分可以修改一下hostname
vim /etc/hostname这里2号机就叫vmCent21号机就叫vmCent1以此类推保存重启就生效了。 1.在/opt/module/apache-zookeeper-3.5.7-bin/zkData里面创建一个叫myid的文件必须是这个名字3台机都要创建这个这个文件。
[rootvmCent1 zkData]# pwd
/opt/module/apache-zookeeper-3.5.7-bin/zkData
[rootvmCent1 zkData]# vim myidmyid文件的内容就是一个唯一标识这里我1号机内容就写12号机内容就写2以此类推。 查看3台机器的ip地址
ifconfig1号机192.168.88.129 2号机192.168.88.130 3号机192.168.88.131
2.打开zoo.cfg修改配置
[rootvmCent1 conf]# pwd
/opt/module/apache-zookeeper-3.5.7-bin/conf
[rootvmCent1 conf]# vim zoo.cfg在zoo.cfg里添加以下内容3台机都要这样做
##########cluster#############
server.1192.168.88.129:2888:3888
server.2192.168.88.130:2888:3888
server.3192.168.88.131:2888:3888server.AB:C:D 配置参数含义 A是一个数字表示这个是第几号服务器 集群模式下配置一个文件 myid 这个文件在 dataDir目录下这个文件里面有一个数据就是 A的值 Zookeeper启动时读取此文件拿到里面的数据与 zoo.cfg里面 的配置信息比较从而判断到底是哪个 server。 B是这个服务器的地址 C是这个服务器 Follower与集群中的 Leader服务器交换信息的端口 D是 万一集群中的 Leader服务器挂了需要一个端口来重新进行选举选出一个新的Leader而这个端口就是用来执行选举时服务器相互通信的端口。 3.启动 1号机启动ZooKeeper
[rootvmCent1 bin]# pwd
/opt/module/apache-zookeeper-3.5.7-bin/bin
[rootvmCent1 bin]# ./zkServer.sh start启动后./zkServer.sh status查看状态
[rootvmCent1 bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Error contacting service. It is probably not running.出现了Error上面提到ZooKeeper集群要有半数以上的机器才能正常运行现在总共3台只启动了1台不够半数当然不能正常运行。 现在在第2台机器上启动ZooKeeper发现还是出现这个Error按道理应该是正常运行才对。先停掉已经启动的ZooKeeper。 这里涉及的端口有2181,2888,3888可能是没开放端口导致有问题的那我们就把他们3个端口都打开试试。 开放2181端口命令其他2个端口把2181替换执行就可以了
firewall-cmd --permanent --add-port2181/tcp开放完要重启防火墙才能生效 firewall-cmd --reload可以查询指定端口是否开放
firewall-cmd --query-port2181/tcp开放了就会显示success 现在我们再启动1号机的ZooKeeper再启动2号机的ZooKeeper。 1号机
[rootvmCent1 bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower1号机可以了变成follower 2号机
[rootvmCent2 bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader2号机也可以了,变成leader。 未正常启动在启动时的目录下会有zookeeper.out文件查看报错信息cat zookeeper.out 3号机启动zookeeper
[rootvmCent3 bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower3号机变成follower ./zkServer.sh status命令查看状态执行了几次都是有Error等一下再执行发现又可以了可能有点延迟
第一次启动的选举机制 假设现在ZooKeeper集群里面有5台服务器这5台服务器都没有启动。 1.启动服务器1发起一次选举。服务器1投自己1票不够半数以上5的一半2.5超过2.5也就是最少要3票选举无法完成服务1状态为LOOKING 2.启动服务器2发起一次选举。服务器1和服务器2分别投自己1票并且告诉对方自己现在有多少票。此时服务器1发现服务器2的myid服务器2的myid2比自己服务器1目前投的人也是服务器1自己因为他把票给了自己的myid大服务器1就更改投票把自己的一票投给服务器2。此时服务器1有0票服务器2有2票也是不够半数以上选举依然无法完成服务器1和服务器2的状态都为LOOKING 3.服务器3启动发起一次选举也是按照上面第2项的流程走一遍所以投票结果服务器1有0票服务器2有0票服务器3有3票此时服务器3 的票数已经超过一半服务器3就成为Leader。服务器1和服务器2的状态都变为FOLLOWING服务器3的状态变为LEADING 4.服务器4启动发起一次选举此时服务器123已经不是LOOKING状态不会更改投票信息 。投票结果服务器3有3票服务器4有1票此时服务器4少数服从多数改变投票信息把自己的1票投给服务器3并更改自己的状态为FOLLOWING 5.服务器5启动与第4项的流程一样少数服从多数。 几个重要信息
SID服务器 ID 。 用来唯一标识一台ZooKeeper 集群中的机器每台机器不能重复 和 myid 一致 。ZXID事务 ID 。 ZXID 是一个事务 ID 用来标识一次服务器状态的变更。 在某一时刻集群中的每台机器的 ZXID 值不一定完全一致这和 ZooKeeper 服务器对于客户端“更新请求”的处理逻辑有关。Epoch每个 Leader 任期的代号 。没有Leader 时同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加。 可以理解为当前集群所处的年代或者周期每个leader就像皇帝都有自己的年号所以每次改朝换代leader变更之后都会在前一个年代的基础上加1。这样就算旧的leader崩溃恢复之后也没有人听他的了因为follower只听从当前年代的leader的命令。
非第一次启动的选举机制 1.当ZooKeeper集群中的1台服务器出现下列情况之一时就会开始Leader选举
服务器初始化启动。就是上一小节服务器运行期间无法和Leader保持连接。比如一台follower服务器挂了 假设服务器5有故障了连不上Leader但是服务器5并不会认为自己故障了而是认为其他的服务器故障了有点搞笑这时服务器5就想要举行一次选举选出1个老大Leader
2.当1台机器进入Leader选举流程时当前集群也可能会处于以下两种状态
集群中已经存在1个Leader。 继续用上面服务器5的例子服务器5想举行一次选举它就会尝试去连接其他服务器包括Leader服务器然后就会被告知当前服务器的 Leader 信息哥们Leader还在呢对于该机器来说仅仅需要和 Leader 机器建立连接并进行状态同步即可。集群中的Leader服务器确实挂了。 假设ZooKeeper 由 5 台服务器组成 SID 分别为 1 、 2 、 3 、 4 、 5 ZXID 分别为 8 、 8 、 8 、 7 、 7 ZXID解释 并且此时 SID 为 3 的服务器是 Leader 。某一时刻3 和 5 服务器出现故障因此开始进行 Leader选举。
SIDEpochZXIDSID118121824174
选举 Leader 规则 ①EPOCH 大的直接胜出 ②EPOCH 相同事务 id 大的胜出 ③事务id 相同服务器 id 大的胜出
集群启动停止脚本
之前每次启动、查看状态、停止ZooKeeper时都要在每台机器上输入命令如果有100台那就很浪费时间了这里写一个脚本一次性操作集群里的全部机器。 安装一个软件sshpass
yum install sshpass使用这个软件只是方便学习 正式生产环境还是用SSH 无密码身份验证吧。 参考https://linux.cn/article-8086-1.html
我在/root目录下创建了一个bin文件夹在这个bin文件夹里创建zk.sh脚本
vim zk.sh把下面的代码复制进去
#!/bin/bashcase $1 in
start){for i in 192.168.88.129 192.168.88.130 192.168.88.131doecho -------------- zookeeper $i 启动 ---------------sshpass -p 1 ssh -o StrictHostKeyCheckingno $i /opt/module/apache-zookeeper-3.5.7-bin/bin/zkServer.sh startdone
}
;;
stop){for i in 192.168.88.129 192.168.88.130 192.168.88.131doecho -------------- zookeeper $i 停止 ---------------sshpass -p 1 ssh -o StrictHostKeyCheckingno $i /opt/module/apache-zookeeper-3.5.7-bin/bin/zkServer.sh stopdone
}
;;
status){for i in 192.168.88.129 192.168.88.130 192.168.88.131doecho -------------- zookeeper $i 状态 ---------------sshpass -p 1 ssh -o StrictHostKeyCheckingno $i /opt/module/apache-zookeeper-3.5.7-bin/bin/zkServer.sh statusdone
}
;;
esacfor i in 192.168.88.129 192.168.88.130 192.168.88.131这里我写的是这3台服务器的ip地址sshpass -p 1这个1是因为我这3台机器的登录密码都是1所以我就填1了这些要根据实际情况填。 保存退出然后修改这个文件的权限
chmod 777 zk.sh执行这个脚本启动ZooKeeper
zk.sh start[rootvmCent1 bin]# zk.sh start
-------------- zookeeper 192.168.88.129 启动 ---------------
Warning: Permanently added 192.168.88.129 (ECDSA) to the list of known hosts.
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
-------------- zookeeper 192.168.88.130 启动 ---------------
Warning: Permanently added 192.168.88.130 (ECDSA) to the list of known hosts.
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
-------------- zookeeper 192.168.88.131 启动 ---------------
Warning: Permanently added 192.168.88.131 (ECDSA) to the list of known hosts.
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED可以看到3台机器都启动了查看状态
zk.sh status[rootvmCent1 bin]# zk.sh status
-------------- zookeeper 192.168.88.129 状态 ---------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
-------------- zookeeper 192.168.88.130 状态 ---------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader
-------------- zookeeper 192.168.88.131 状态 ---------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower可以看到服务器12都是follower服务器3是leader我直接在服务器2的终端里查看状态也确实是leader
[rootvmCent2 bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader停止ZooKeeper集群
zk.sh stop[rootvmCent1 bin]# zk.sh stop
-------------- zookeeper 192.168.88.129 停止 ---------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Stopping zookeeper ... STOPPED
-------------- zookeeper 192.168.88.130 停止 ---------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Stopping zookeeper ... STOPPED
-------------- zookeeper 192.168.88.131 停止 ---------------
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/module/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Stopping zookeeper ... STOPPED也是可以的。
客户端命令行操作
先启动集群再启动客户端
./zkCli.sh -server ip地址:2181如果你想连集群中的服务器2就把服务器2的ip地址写到上面ip地址的位置2181是默认端口号。
客户端命令行语法
基本语法描述help显示所有操作命令ls path使用 ls 命令查看当前 znode 的子节点-w 监听子节点变化-s 附加次级信息create普通创建-s 含有序列-e 临时重启或者超时消失get path获得节点的值【可监听】-w 监听节点内容变化-s 附加次级信息set设置节点的值stat查看节点状态delete删除节点deleteall递归的删除节点查看当前znode中所包含的内容
ls /查看当前节点详细数据
ls s /[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 1czxid创建节点的事务 zxid 每次修改ZooKeeper状态都会 产生一个 ZooKeeper事务 ID。事务 ID是 ZooKeeper中所有修改总的次序。每 次 修改都有唯一的 zxid如果 zxid1小于 zxid2那么 zxid1在zxid2之前发生。
ctimeznode被创建的毫秒数从 1970年开始
mzxidznode最后更新的事务 zxid
mtimeznode最后修改的毫秒数从 1970年开始
pZxidznode最后更新的子节点 zxid cversionznode 子节点变化号znode 子节点修改次数 dataversionznode 数据变化号 aclVersionznode 访问控制列表的变化号 ephemeralOwner如果是临时节点这个是znode 拥有者的session id。如果不是 临时节点则是0。 dataLengthznode 的数据长度 numChildrenznode 子节点数量
节点类型 演示
客户端连接集群中ip地址为192.168.88.129的服务器 ./zkCli.sh -server 192.168.88.129:2181连接后光标左边就变为这样
[zk: 192.168.88.129:2181(CONNECTED) 0] 查看节点
[zk: 192.168.88.129:2181(CONNECTED) 0] ls /
[zookeeper]create命令创建1个永久不带序号的节点
[zk: 192.168.88.129:2181(CONNECTED) 1] create /school schoolOne这里我创建了1个school节点给它赋值为schoolOne 再次查看节点
[zk: 192.168.88.129:2181(CONNECTED) 2] ls /
[school, zookeeper]可以看到多了school节点。 在school节点下再创一个gradeOne节点并查看
[zk: 192.168.88.129:2181(CONNECTED) 3] create /school/gradeOne NumberOne
Created /school/gradeOne
[zk: 192.168.88.129:2181(CONNECTED) 4] ls /school
[gradeOne]get -s命令获取节点的值和相关信息
[zk: 192.168.88.129:2181(CONNECTED) 5] get -s /school
schoolOne
cZxid 0x800000002
ctime Fri Sep 03 17:33:00 CST 2021
mZxid 0x800000002
mtime Fri Sep 03 17:33:00 CST 2021
pZxid 0x800000003
cversion 1
dataVersion 0
aclVersion 0
ephemeralOwner 0x0
dataLength 9
numChildren 1create -s创建1个永久带序号的节点
[zk: 192.168.88.129:2181(CONNECTED) 6] create -s /school/gradeOne/tom myNameTom
Created /school/gradeOne/tom0000000000可以看到tom节点自动加了0000000000序号。 如果我再创建1个tom节点
[zk: 192.168.88.129:2181(CONNECTED) 7] create -s /school/gradeOne/tom myNameTom
Created /school/gradeOne/tom0000000001可以看到tom后面的序号自动加1 查看gradeOne节点
[zk: 192.168.88.129:2181(CONNECTED) 8] ls /school/gradeOne
[tom0000000000, tom0000000001]也就是说带序号的节点可以重复创建他会自动增加序号。 如果我们再创建gradeOne节点
[zk: 192.168.88.129:2181(CONNECTED) 9] create /school/gradeOne
Node already exists: /school/gradeOne提示节点已存在因为当时创建gradeOne节点是不带序号的所以不能重复创建。 带序号的节点可以重复的创建多个带序号的节点之间使用序号来区分不带序号的节点不能重复创建只能创建一次 quit命令退出客户端再重新开启客户端看看之前创建的节点还在不在
[zk: 192.168.88.129:2181(CONNECTED) 0] ls /
[school, zookeeper]
[zk: 192.168.88.129:2181(CONNECTED) 1] ls /school
[gradeOne]
[zk: 192.168.88.129:2181(CONNECTED) 2] ls /gradeOne
Node does not exist: /gradeOne
[zk: 192.168.88.129:2181(CONNECTED) 3] ls /school/gradeOne
[tom0000000000, tom0000000001]依然存在。 create -e创建临时不带序号的节点
[zk: 192.168.88.129:2181(CONNECTED) 6] create -e /school/gradeTwo NumberTwo
Created /school/gradeTwoquit命令退出客户端再重新开启客户端看看这个临时节点还在不在
[zk: 192.168.88.129:2181(CONNECTED) 0] ls /school
[gradeOne]可以看到已经不在了。 set命令修改节点的值。先看看school的值
[zk: 192.168.88.129:2181(CONNECTED) 1] get -s /school
schoolOne
cZxid 0x800000002
ctime Fri Sep 03 17:33:00 CST 2021
mZxid 0x800000002
mtime Fri Sep 03 17:33:00 CST 2021
pZxid 0x80000000c
cversion 5
dataVersion 0
aclVersion 0
ephemeralOwner 0x0
dataLength 9
numChildren 1修改school节点的值并查看
[zk: 192.168.88.129:2181(CONNECTED) 2] set /school schoolTwo
[zk: 192.168.88.129:2181(CONNECTED) 3] get -s /school
schoolTwo
cZxid 0x800000002
ctime Fri Sep 03 17:33:00 CST 2021
mZxid 0x80000000e
mtime Fri Sep 03 18:02:34 CST 2021
pZxid 0x80000000c
cversion 5
dataVersion 1
aclVersion 0
ephemeralOwner 0x0
dataLength 9
numChildren 1修改成功。
监听器
客户端注册监听它关心的目录节点当目录节点发生变化数据改变、节点删除、子目录节点增加删除时ZooKeeper 会通知客户端。监听机制保证ZooKeeper 保存的任何的数据的任何改变都能快速的响应到监听了该节点的应用程序。 在服务器1,2都开启客户端这里就不指定server了
./zkCli.sh监听节点的值有没有变化
[zk: localhost:2181(CONNECTED) 1] get -s /school
schoolTwo
cZxid 0x800000002
ctime Fri Sep 03 17:33:00 CST 2021
mZxid 0x80000000e
mtime Fri Sep 03 18:02:34 CST 2021
pZxid 0x80000000c
cversion 5
dataVersion 1
aclVersion 0
ephemeralOwner 0x0
dataLength 9
numChildren 1现在/school节点的值为schoolTwo现在设置在服务器1的客户端那里监听/school节点的值 get -w /school在服务器2的客户端那里修改/school的值为SchoolThree修改后在服务器1的客户端就会显示
[zk: localhost:2181(CONNECTED) 3]
WATCHER::WatchedEvent state:SyncConnected type:NodeDataChanged path:/school监听到节点数据发生变化。但是在服务器2的客户端那里再次修改/school的值这时服务器1的客户端是不会监听到数据变化的他只会监听1次而已。
监听节点的子节点有没有变化
现在我们在school节点下只有1个gradeOne子节点
[zk: localhost:2181(CONNECTED) 5] ls /school
[gradeOne]
服务器1的客户端监听节点的子节点有没有变化
ls -w /school在服务器2的客户端里在school节点下创建一个gradeTwo子节点
create /school/gradeTwo回到服务器1的客户端那边就会监听到
[zk: localhost:2181(CONNECTED) 7]
WATCHER::WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/school这种监听和监听数据变化一样注册监听1次就生效1次后续在监听节点下再创建子节点就不会监听到了。
删除节点
delete /school/gradeOne如果要删除的节点下还有子节点是不能删除这个节点的会提示
[zk: localhost:2181(CONNECTED) 7] delete /school/gradeOne
Node not empty: /school/gradeOne如果要删除的节点下还有子节点要用deleteall
deleteall /school/gradeOne查看节点状态
stat /school跟这个命令相似但是不显示节点的数据
get -s /school客户端API操作
客户端API-创建节点
首先创建maven项目导入依赖 dependenciesdependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversionRELEASE/version/dependencydependencygroupIdorg.apache.logging.log4j/groupIdartifactIdlog4j-core/artifactIdversion2.8.2/version/dependencydependencygroupIdorg.apache.zookeeper/groupIdartifactIdzookeeper/artifactIdversion3.5.7/version/dependency/dependenciesresources文件夹下创建log4j.properties用于打印日志设置
log4j.rootLoggerINFO, stdout
log4j.appender.stdoutorg.apache.log4j.ConsoleAppender
log4j.appender.stdout.layoutorg.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern %d %p [%c] - %m%n
log4j.appender.logfileorg.apache.log4j.FileAppender
log4j.appender.logfile.Filetarget/spring.log
log4j.appender.logfile.layoutorg.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern%d %p [%c] - %m%n创建个测试类
package com.rgb3;import org.apache.zookeeper.*;
import org.junit.Before;
import org.junit.Test;/*** author tony* date 2021/9/3 21:18*/
public class ZkClient {/*每个地址的左右不能有空格*这3个ip地址和端口要根据自己实际情况填这里我就把3台虚拟机的ip填上*/private String connectString 192.168.88.129:2181,192.168.88.130:2181,192.168.88.131:2181;private int sessionTimeOut 60000; //电脑慢的可以试试值大一点private ZooKeeper zkClient;Beforepublic void init() throws Exception{zkClient new ZooKeeper(connectString, sessionTimeOut, new Watcher() {public void process(WatchedEvent watchedEvent) {}});}Testpublic void create() throws Exception{//这里创建1个/company节点如果集群里已经有这个节点测试会失败的String s zkClient.create(/company, idea.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}
}ZooKeeper.create()参数 1.节点路径 2.节点数据 3.节点权限 4.节点类型永久/临时带序号/不带序号
客户端API-监听节点变化
public class ZkClient {//每个地址的左右不能有空格private String connectString 192.168.88.129:2181,192.168.88.130:2181,192.168.88.131:2181;private int sessionTimeOut 60000;private ZooKeeper zkClient;Beforepublic void init() throws Exception{zkClient new ZooKeeper(connectString, sessionTimeOut, new Watcher() {public void process(WatchedEvent watchedEvent) {System.out.println(-----------------process-start-----------------------);ListString children null;try {children zkClient.getChildren(/, true);} catch (Exception e) {e.printStackTrace();}for (String child : children) {System.out.println(监听到的子节点child);}System.out.println(-----------------process-end-----------------------);}});}Testpublic void get() throws Exception{System.out.println(-----------------get-start-----------------------);ListString children zkClient.getChildren(/, true);for (String child : children){System.out.println(获取到的子节点child);}System.out.println(-----------------get-end-----------------------);//为了程序结束后不关闭设置线程睡眠Thread.sleep(Long.MAX_VALUE);}
}getChildren()参数 1.获取哪个路径的子节点 2.true就表示用上面init()方法里的Watcher监听也可以直接在这里new Watcher也可以。 get()方法里Thread.sleep(Long.MAX_VALUE);让主线程睡眠了但注册线程和监听线程并没有睡。 在get()方法里获取/节点的子节点并注册监听/节点然后就打印出获取到的子节点名字出来当/节点增加/减少了子节点时就会被监听到进入到Watcher里的process()方法根据上面的小节知道注册监听1次就生效一次当/节点增加/减少了子节点时就监听到1次后面/节点再增加/减少了子节点就不再监听了所以为了能无限次监听到/节点的子节点有没有变化就在process()方法里再次获取/节点的子节点并监听
children zkClient.getChildren(/, true);运行后控制台打印
-----------------get-start-----------------------
2021-09-04 15:10:44,616 INFO [org.apache.zookeeper.ClientCnxn] - Opening socket connection to server 192.168.88.130/192.168.88.130:2181. Will not attempt to authenticate using SASL (unknown error)
2021-09-04 15:10:44,620 INFO [org.apache.zookeeper.ClientCnxn] - Socket connection established, initiating session, client: /192.168.88.1:3879, server: 192.168.88.130/192.168.88.130:2181
2021-09-04 15:10:44,628 INFO [org.apache.zookeeper.ClientCnxn] - Session establishment complete on server 192.168.88.130/192.168.88.130:2181, sessionid 0x20000ba53510002, negotiated timeout 40000
-----------------process-start-----------------------
获取到的子节点market
获取到的子节点shop
获取到的子节点zookeeper
获取到的子节点town
获取到的子节点school
获取到的子节点company
获取到的子节点hospital
-----------------get-end-----------------------
监听到的子节点market
监听到的子节点shop
监听到的子节点zookeeper
监听到的子节点town
监听到的子节点school
监听到的子节点company
监听到的子节点hospital
-----------------process-end-----------------------可以看到运行后监听器和get()方法都有打印内容出来说明客户端与服务端连接时也会触发一次监听。 在虚拟机那边开一个客户端在/节点下创建一个drink节点
create /drink在idea控制台这边能看到打印
-----------------process-start-----------------------
监听到的子节点market
监听到的子节点shop
监听到的子节点zookeeper
监听到的子节点town
监听到的子节点school
监听到的子节点company
监听到的子节点hospital
监听到的子节点drink
-----------------process-end-----------------------然后在客户端那里删除1个hospital节点
delete /hospital在idea控制台这边能看到打印
-----------------process-start-----------------------
监听到的子节点market
监听到的子节点shop
监听到的子节点zookeeper
监听到的子节点town
监听到的子节点school
监听到的子节点company
监听到的子节点drink
-----------------process-end-----------------------客户端API-节点是否存在 Beforepublic void init() throws Exception{zkClient new ZooKeeper(connectString, sessionTimeOut, new Watcher() {public void process(WatchedEvent watchedEvent) {}});}Testpublic void exist() throws Exception{Stat stat zkClient.exists(/company, false);System.out.println(stat null ? 不存在 : 存在);}exists()参数 1.查询哪个节点是否存在 2.是否监听这里false就不监听了简单点。
写入请求发送流程
客户端要向集群写入数据时他向集群发请求有可能访问到Leader也有可能访问到Follower以下是两种流程
写入请求发送给Leader节点的流程 写入请求发送给Follower节点的流程 服务器动态上下线 上图说的是一个层面从另一个层面来说对于ZooKeeper集群其他任何的都是Client是有点绕。图中的服务器123对于ZooKeeper集群来说也是Client只不过他向ZooKeeper集群发送的是create请求创建节点图中下半部分的“客户端”123对于ZooKeeper集群来说也是Client只不过他向ZooKeeper集群发送的是get请求获取节点信息还可以监听节点。 ZkClient
public class ZkClient {//每个地址的左右不能有空格private String connectString 192.168.88.129:2181,192.168.88.130:2181,192.168.88.131:2181;private int sessionTimeOut 60000;private ZooKeeper zkClient;public void getConnect() throws Exception{zkClient new ZooKeeper(connectString, sessionTimeOut, new Watcher() {public void process(WatchedEvent watchedEvent) {try {getServerList();//为了再次注册监听} catch (Exception e) {e.printStackTrace();}}});}//获取服务器列表public void getServerList() throws Exception{//获取服务器列表并监听/server节点ListString children zkClient.getChildren(/servers, true);//存储节点数据的列表ArrayListString serverData new ArrayList();//获取节点中的数据这里每个节点存的是名字for (String child : children){//第三个参数null就是不需要其他信息byte[] data zkClient.getData(/servers/ child, false, null);serverData.add(new String(data));}System.out.println(serverData);}//模拟业务功能public void business() throws Exception{System.out.println(client正在处理业务);//模拟处理业务需要很长时间Thread.sleep(Long.MAX_VALUE);}
}ZkServer
public class ZkServer {private String connectString 192.168.88.129:2181,192.168.88.130:2181,192.168.88.131:2181;private int sessionTimeOut 60000;private ZooKeeper zkClient;public void getConnect() throws Exception{zkClient new ZooKeeper(connectString, sessionTimeOut, new Watcher() {public void process(WatchedEvent watchedEvent) {}});}//注册public void register(String hostname,String data) throws Exception{//想集群注册其实就是在集群里创建一个节点这里创建的的是临时带序号的节点String s zkClient.create(/servers/hostname, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);}//模拟业务功能public void business(String hostname) throws Exception{System.out.println(hostname正在运行);//模拟处理业务需要很长时间Thread.sleep(Long.MAX_VALUE);}
}ZkMain
public class ZkMain {public static void main(String[] args) throws Exception{ZkServer zkServer new ZkServer();ZkClient zkClient new ZkClient();zkClient.getConnect();zkClient.getServerList();zkClient.business();/*String name server1;zkServer.getConnect();zkServer.register(name);zkServer.business(name);*/}
}命令行操作
先启动main()方法 idea控制台打印
[]
client正在处理业务
[]使用虚拟机那边的客户端在ZooKeeper集群的/节点创建一个/servers节点在/servers节点下创建1个临时带序号节点/servers/server1
create -e -s /servers/server1 machine1idea控制台打印
[machine1]然后删除刚刚创建的/servers/server1节点
delete /servers/server10000000003idea控制台
[]idea操作
修改ZkMain
public class ZkMain {ZkServer zkServer new ZkServer();ZkClient zkClient new ZkClient();Testpublic void testClient() throws Exception{zkClient.getConnect();zkClient.getServerList();zkClient.business();}Testpublic void testServer() throws Exception{String name server1;String data machine1;zkServer.getConnect();zkServer.register(name,data);zkServer.business(name);}
}先启动testClient()testClient()的控制控制台
[]
client正在处理业务
[]再启动testServer()testServer() 的控制台
server1正在运行再看看testClient()的控制控制台
[machine1]curator框架实现分布式锁
什么叫做分布式锁呢 比如说进程 1想使用该资源的时候会先去获得锁进程 1获得锁以后会对该资源保持独占这样其他进程就无法访问该资源进程1用完该资源以后就将锁释放掉让其他进程来获得锁那么通过这个锁机制我们就能保证了分布式系统中多个进程能够有序的访问该临界资源。那么我们把这个分布式环境下的这个锁叫作分布式锁。 可以自己按照上图来写一个操作分布式锁的例子但是情况可以很多和繁琐可以用curator框架来实现。 添加依赖 dependencygroupIdorg.apache.curator/groupIdartifactIdcurator-recipes/artifactIdversion4.3.0/version/dependency创建一个类演示
public class ZkLock {public static void main(String[] args) {CuratorFramework zkClient getZkClient();String lockPath /locks;InterProcessMutex lock1 new InterProcessMutex(zkClient, lockPath);InterProcessMutex lock2 new InterProcessMutex(zkClient, lockPath);new Thread(new Runnable() {public void run() {try {lock1.acquire();//获取到锁System.out.println(线程1---获取到锁);Thread.sleep(2000);lock1.release();//释放锁System.out.println(线程1---释放锁);} catch (Exception e) {e.printStackTrace();}}}).start();new Thread(new Runnable() {public void run() {try {lock2.acquire();//获取到锁System.out.println(线程2---获取到锁);Thread.sleep(2000);lock2.release();//释放锁System.out.println(线程2---释放锁);} catch (Exception e) {e.printStackTrace();}}}).start();}private static CuratorFramework getZkClient(){String address 192.168.88.129:2181,192.168.88.130:2181,192.168.88.131:2181;//3秒后重试重试3次ExponentialBackoffRetry retry new ExponentialBackoffRetry(3000, 3);CuratorFramework zkClient CuratorFrameworkFactory.builder().connectString(address).sessionTimeoutMs(5000).connectionTimeoutMs(20000).retryPolicy(retry).build();//启动客户端zkClient.start();return zkClient;}
}在集群里创建好/locks节点 启动演示看控制台打印
线程2---获取到锁
线程2---释放锁
线程1---获取到锁
线程1---释放锁