前言:本文为原创 若有错误欢迎评论!
一.安装
1.linux安装(非Docker)
-
下载zookeeper-3.4.14.tar.gz(https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/)
链接:https://pan.baidu.com/s/1DrmzdxgCCGjBHUwTKBehPQ
提取码:p959
复制这段内容后打开百度网盘手机App,操作更方便哦 -
上传到虚拟机并解压 解压后 进入conf 修改为zoo.cfg
-
在zookeeper下新建一个data目录 将zoo.cfg的dataDir修改为该data目录底下的地址
-
进入bin目录启动: ./zkServer.sh start
-
开放端口:2181
2.linux安装(Docker)
-
拉取zk镜像
docker pull zookeeper:3.5
-
创建容器
docker create --name zk -p 2181:2181 zookeeper:3.5
-
启动容器
docker start zk
3.windows管控台
-
下载
链接:https://pan.baidu.com/s/14ogQivPWgLe0bPqcrJN-sQ
提取码:3phj
复制这段内容后打开百度网盘手机App,操作更方便哦 -
运行:ZooInspector\build\start.bat
-
点击左上角运行 并改为正确的地址 然后点击"OK"
二. Zookeeper基础
1.常用命令
- 创建节点
create [-s] [-e] path data acl
( -s表示创建顺序节点 -e表示创建临时节点 data表示创建的节点的数据内容)
- 查看节点
ls path (获取节点的子节点)
get path (获取节点的数据)
stat path (查看节点状态)
ls2 path (获取节点的子节点以及当前节点的状态)
- 修改节点
set path data [version]
注意:version必须等于当前的dataversion 相当于一个乐观锁 每次修改一次version会自增一次 所以多个线程可以同时操作 但由于version被一个线程操作后会自增 而其他线程用的version还是旧的version就不会操作成功
- 删除节点
delete path [version] :如果此时该节点有子节点,则不允许删除
rmr path :递归删除整个节点
注意:所有的path必须从跟根路径开始 都是‘/path‘的形式
2.session机制
- 用于客户端与服务端之间的连接,可设置超时时间,通过心跳包的机制(客户端向服务端ping包请求)检查心 跳结束,session就过期
- session过期的时候,该session创建的所有临时节点都会被抛弃
注意:使用close直接关闭客户端的话 链接直接断掉 子节点也直接删除 但是客户端下线的话在超时时间内上线不会删除临时子节点
3.watcher机制
- 监视单一节点(对子节点无效 增删改自己的数据会触发):
get path watch
stat path watch
- 监视子节点(只有字典点增删有效 修改子节点数据不会触发):
ls path watch
ls2 path watch
- 注意:所有的watch都是一次性的 触发一次之后就会失效
二.Zookeeper集群
- 端口:2181+2888 3888(后两个用于自己配置的集群之间通信)
非Docker搭建
1.先给一个虚拟机安装好zookeeper
2.修改配置文件名为zoo.cfg
3.编辑zoo.cfg
修改:
dataDir=/usr/local/linux/zookeeper/data
添加:
server.1=ip1:2888:3888 server.2=ip2:2888:3888 server.3=ip3:2888:3888
4.在自己新建的data目录下新建myid(没有’.'文件的后缀)
5.把zookeeper的解压包拷贝到三个虚拟机
6.编辑每个虚拟机的myid文件 内容均只有一个数字(表示myid) 依次为1、2、3
7.开放2181 2888 3888端口
8.依次启动 ./zkServer start
9.查看启动和节点的状态:./zkServer status (会显示每个节点的Mode)
Docker搭建
1.拉取zk镜像
- 注意:版本要和服务端引入的zk的版本一致
docker pull zookeeper:3.4
2.创建容器
docker create --name zk01 --net host \
-e ZOO_MY_ID=1 \
-e ZOO_SERVERS="server.1=192.168.1.7:2888:3888 server.2=192.168.1.18:2888:3888 server.3=192.168.1.19:2888:3888" \
zookeeper:3.4
docker create --name zk02 --net host \
-e ZOO_MY_ID=2 \
-e ZOO_SERVERS="server.1=192.168.1.7:2888:3888 server.2=192.168.1.18:2888:3888 server.3=192.168.1.19:2888:3888" \
zookeeper:3.4
docker create --name zk03 --net host \
-e ZOO_MY_ID=3 \
-e ZOO_SERVERS="server.1=192.168.1.7:2888:3888 server.2=192.168.1.18:2888:3888 server.3=192.168.1.19:2888:3888" \
zookeeper:3.4
3.三个虚拟机分别启动容器
docker start zk01 && docker logs -f zk01
docker start zk02 && docker logs -f zk02
docker start zk03 && docker logs -f zk03
三.分布式锁
- 原因:
单机的锁没法满足(即本地的每一个线程安全)没法保证多台机子部署的每一个线程安全 - 原理:
- zookeeper:通过能否创建子节点来判断 线程是否拿到锁(必须设置为-e 临时节点 因为一旦宕机后会断开session 然后释放掉锁 不然永远不会释放锁)
- redis:通过是否可以setnx判断 线程是否拿到锁(必须再setex设置时间 不然宕机后无法释放锁)
1.Zookeeper依赖:
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
2.使用:
- 定义全局变量
ZooKeeper zooKeeper
CountDowmLatch countDownLatch;
- 在构造方法里:
private ZkLock() {
try {
//获取zookeeper对象
zooKeeper = new ZooKeeper("192.1.1.101:2181,192.1.1.102:2181,192.1.1.103:2181", 5000, new ZkWatcher());
try {
//让线程等待watcher确认连接
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
- 新建一个内部类 implements Watcher 重写process()方法
private class ZkWatcher implements Watcher {
@Override
public void process(WatchedEvent event) {
System.out.println("接收到监听事件=====》"+event);
if (Event.KeeperState.SyncConnected == event.getState()) {
// 判断是否连接上 然后不让countDownLatch再等待
countDownLatch.countDown();
}
}
}
- 定义加锁的方法 使用zookeeper.create()方法创建子节点
当前线程若是没拿到锁创建失败会抛出异常 在异常中让当前线程睡眠 然后继续尝试拿锁
public void lock(Integer id) {
String path = "/xdclass-product-lock-" + id;
//创建临时节点,如果创建成功的话,就表示获取锁,如果失败,则不断尝试
while(true){
try {
zooKeeper.create(path,"".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
} catch (Exception e) {
try {
Thread.sleep(5000);
continue;
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
break;
}
}
5.释放锁 调用zookeeper.delete()
public void unLock(Integer id) {
String path = "/xdclass-product-lock-" + id;
try {
zooKeeper.delete(path,-1);
} catch (Exception e) {
e.printStackTrace();
}
}
注意:存储的节点的名字要有区分性 且zookeeper.create()的path不能包含子路径 只能是一层路径