深入浅出 zookeeper (1)
什么是zookeeper
他是个数据库,文件存储系统,并且有监听通知机制(观察者模式)
存了什么
节点 (四种节点)
zookeeper 与nacos的区别
1.集群不同
zookeeper是主从的集群(类似一写多读) 集群中zk在数据更新的时候,通过leader节点将将消息广播给其他follower节点,采用简单的两阶段提交模式当超过一半的follower节点响应可以提交就更新代码。当leader挂了,或者超半数follower投票得出leader不可用,那么会重新选举,这段期间zk服务是不可用的
而nacos则是并行的集群(多写多读) 就算其中一个挂了还是可以继续读写
2.低沉代码不同
Zookeeper的功能主要是它的树形节点来实现的当有数据变化的时候或者节点过期的时候,会通过事件触发通知对应的客户端数据变化了,然后客户端再请求zk获取最新数据,采用push-pull来做数据更新。
Nacos的配置中心和注册中心实现的是两套代码,和Zk不同,依赖Mysql数据库做数据存储,当有数据更新的时候,直接更新数据库的数据,然后将数据更新的信息异步广播给Nacos集群中所有服务节点数据变更
3.消息发送,
一个有采用过半机制保持一致性,另外一个异步广播,通过后台线程重试保证。
4.功能利弊
个人感觉nacos更善于做服务注册和监控 而zookeeper也可做分布式锁(重量级)
zookeeper的一般用途
他主要的应用场景有以下几个:
- 服务注册与订阅(共用节点)
- 分布式通知(监听znode)
- 服务命名(znode特性)
- 数据订阅、发布(watcher)
- 分布式锁(临时节点)
zookeeper的搭建
docker 建立独立Zookeeper容器
docker hub 查看版本
https://hub.docker.com/_/zookeeper
拉去镜像
zookeeper部署后, 3.5以后的版本, 会自动占用8080端口. 需要修改配置文件更改服务器端口。否则zk服务器启动不起来。已踩坑 建议下载3.5以上的版本
admin.serverPort=你随便设置一个没有被用的就好了
docker pull zookeeper:3.4
查看镜像
docker images
运行镜像
docker run --name zookeeper -p 2181:2181 -d zookeeper:3.4
--name:指定容器名字
-p:为容器暴露出来的端口分配端口号 前者宿主机 后者容器内的端口
-d:在后台运行容器并打印容器ID
查看运行情况
docker ps -a
进入容器
docker exec -it zookeeper /bin/bash
启动zk服务端
./bin/zkServer.sh start
启动zk客户端
./bin/zkCli.sh
查看
ls /
出现[zookeeper]即可
docker 搭建zookeeper集群
搭建docker网络
$ docker network create --driver bridge --subnet 172.23.0.0/25 --gateway 172.23.0.1 myzk
$ docker network ls
下载docker-compose
# 获取脚本
curl -L https://github.com/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
$ curl -L https://github.com/docker/compose/releases/download/1.25.0-rc2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# 赋予执行权限
$chmod +x /usr/local/bin/docker-compose
创建 并且编写docker-compose.yml
version: '2'
services:
zoo1:
image: zookeeper:3.4 # 镜像名称
restart: always # 当发生错误时自动重启
hostname: zoo1
container_name: zoo1
privileged: true
ports: # 端口
- 2181:2181
volumes: # 挂载数据卷
- ./zoo1/data:/data
- ./zoo1/datalog:/datalog
- ./zoo1/conf:/conf
environment:
TZ: Asia/Shanghai
ZOO_MY_ID: 1 # 节点ID
ZOO_PORT: 2181 # zookeeper端口号
ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888 # zookeeper节点列表
networks:
default:
ipv4_address: 172.23.0.11
zoo2:
image: zookeeper:3.4
restart: always
hostname: zoo2
container_name: zoo2
privileged: true
ports:
- 2182:2181
volumes:
- ./zoo2/data:/data
- ./zoo2/datalog:/datalog
- ./zoo2/conf:/conf
environment:
TZ: Asia/Shanghai
ZOO_MY_ID: 2
ZOO_PORT: 2181
ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
networks:
default:
ipv4_address: 172.23.0.12
zoo3:
image: zookeeper:3.4
restart: always
hostname: zoo3
container_name: zoo3
privileged: true
ports:
- 2183:2181
volumes:
- ./zoo3/data:/data
- ./zoo3/datalog:/datalog
- ./zoo3/conf:/conf
environment:
TZ: Asia/Shanghai
ZOO_MY_ID: 3
ZOO_PORT: 2181
ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
networks:
default:
ipv4_address: 172.23.0.13
networks:
default:
external:
name: myzk
执行命令
docker-compose up -d
zookeeper 机制
选举机制
创建节点获取节点
./bin/zkCli.sh
永久节点
create /(name) “myname”
带序号的节点
create -s /(name)/(name) “myname”
临时节点
create -e /(name) “myname”
带序号的临时节点
create -e -s /(name)/(name) “myname”
修改
set /(name)/(name) “myname”
监听器
get -w /(name)
分布式锁
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
public class ZooKeeperTest {
public static void main(String[] args) {
// 创建分布式锁1
InterProcessMutex lock1 = new InterProcessMutex(getCuratorFramework(), "/locks");
// 创建分布式锁2
InterProcessMutex lock2 = new InterProcessMutex(getCuratorFramework(), "/locks");
new Thread(new Runnable() {
@Override
public void run() {
try {
lock1.acquire();
System.out.println("线程1 获取到锁");
lock1.acquire();
System.out.println("线程1 再次获取到锁");
Thread.sleep(5 * 1000);
lock1.release();
System.out.println("线程1 释放锁");
lock1.release();
System.out.println("线程1 再次释放锁");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock2.acquire();
System.out.println("线程2 获取到锁");
lock2.acquire();
System.out.println("线程2 再次获取到锁");
Thread.sleep(5 * 1000);
lock2.release();
System.out.println("线程2 释放锁");
lock2.release();
System.out.println("线程2 再次释放锁");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
private static CuratorFramework getCuratorFramework() {
ExponentialBackoffRetry policy = new ExponentialBackoffRetry(3000, 3);
CuratorFramework client = CuratorFrameworkFactory.builder().connectString("ip:2181,ip:2182,ip:2183")
.connectionTimeoutMs(2000)
.sessionTimeoutMs(2000)
.retryPolicy(policy).build();
// 启动客户端
client.start();
System.out.println("zookeeper 启动成功");
return client;
}
}