标签:zookeeper zk Zookeeper curator 介绍 2181 节点
一、Zookeeper 介绍
1.什么是 Zookeeper
Zookeeper 是一种分布式协调服务用于管理大型主机,分布式环境中协调管理服务是一个复杂的过程,Zookeeper 通过其简单的架构和 API 解决这个问题。Zookeeper 允许开发人员专注于核心应用程序逻辑,而不必担心应用程序的分布特性。
2.Zookeeper 的应用场景
在分布式系统中,需要有 Zookeeper 作为分布式协调组件,协调分布系统中的状态。
Zookeeper 在实现分布式锁上,可以做到强一致性,CP(一致性,分区容错性)
二、搭建 Zookeeper 服务器
1.配置文件说明
# The number of milliseconds of each tick # 每次心跳时间 tickTime=2000 # The number of ticks that the initial # synchronization phase can take # 允许 follower 链接初始化连接到 leader 最大时间 它表示 tickTime 的时间倍数 initLimit=10 # The number of ticks that can pass between # sending a request and getting an acknowledgement # 允许 follower 与 leader 数据同步最大时间 它表示 tickTime 的时间倍数 syncLimit=5 # the directory where the snapshot is stored. # do not use /tmp for storage, /tmp here is just # example sakes. # 数据保存目录,(如果没有指定dataLogDir也保存到这个目录) dataDir=/opt/homebrew/var/run/zookeeper/data # the port at which the clients will connect # 对客户端提供的端口号 clientPort=2181 # the maximum number of client connections. # increase this if you need to handle more clients # 单个客户端 zookeeper 允许连接端最大数量 #maxClientCnxns=60 # # Be sure to read the maintenance section of the # administrator guide before turning on autopurge. # # http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance # # The number of snapshots to retain in dataDir # 保存数据的快照数量,超过会被删除 #autopurge.snapRetainCount=3 # Purge task interval in hours # Set to "0" to disable auto purge feature # 自动触发清除任务的时间间隔(单位小时),0为不清理 #autopurge.purgeInterval=1 ## Metrics Providers # # https://prometheus.io Metrics Exporter #metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider #metricsProvider.httpPort=7000 #metricsProvider.exportJvmInfo=true
2.相关命令
# 启动 zookeeper 服务 zkServer start # 查看 zookeeper 状态 zkServer status # 停止 zookeeper 服务 zkServer stop
3.docker 搭建单机 zookeeeper
# 获取镜像 docker pull zookeeper # 安装 zookeeper docker run --name zookeeper-1 --restart always -d zookeeper # 进入 zk 命令窗口 docker exec -it 镜像ID zkCli.sh
三、Zookeeper 内部数据模型
1.zk 是如何保存数据的
zk 中的数据是保存在节点上的,节点就是 znode,多个 zonde 之间构成一颗树的目录结构。
树是由节点组成,Zookeeper 的数据存储也同样是基于节点,这种节叫做 Znode。但是,不同于树的节点,Znode 的引用方式是 路径引用,类似文件路径。
命令:
# 查看节点 ls # 创建根节点 test create /test1 # 创建节点并设置 值 create /test1/sub1 abc # 获取节点值 get /test1/sub1 abc
这样的层次结构,让每一个 Znode 节点拥有唯一的路径,就像命名空间一样对不同信息作出清晰的隔离。
2.zk 中的 znode 结构
zk 中的 znode,包含了四个部分:
- data:保存数据
- acl:权限
- c:create 创建权限,允许在该节点下创建子节点
- w:write 更新权限,允许更新该节点的数据
- r:read 读取权限,允许读取节点的内容以及子节点的列表信
- d:delete 删除权限,允许删除该节点的子节点
- a:admin 管理者权限,允许对该节点进行 acl 权限设置
- stat:描述当前 znode 的元数据
- child:当前节点的子节点
3.zk 中节点 znode 类型
- 持久节点:创建出的节点,在会话结束后依然存在。保存数据
create /test
- 持久有序节点:创建出的节点根据先后顺序,会在节点子后带上一个数值,越后执行数值越大,适用于分布式锁的应用场景——单调递增
create -s /test
- 临时节点:临时节点是在会话结束后,自动被删除的,通过这个特性,zk 可以实现服务注册与发现效果。那么临时节点如何维持心跳呢?
create -e /test
- 临时序号节点:跟持久序号节点相同,适用于临时的分布式锁。
create -e -s /test
- Container 节点(3.5.3版本新增):Container 容器节点,当容器中没有任何子节点,该容器节点会被 zk 定期删除(60s)。
create -c /test
- TTL 节点:可以指定节点到期时间,到期后被 zk 定时删除。只通过系统配置 zookeeper.extendedTypesEnabled = true 开启。
4.zk 的数据持久化
zk 的数据是运行在内存中,zk 提供了两种持久化机制:
zk 把执行的命令以日志的方式保存在 dataLogDir 指定的路径中的文件中(如果没有指定 dataLogDir,则按 dataDir 指定的路径)。
zk 会在一定的时间间隔内做一次内存数据的快照,把该时刻的内存数据保存在文件中。
zk 通过两种形式的持久化,在恢复时先恢复快照文件中的数据到内存中,再用日志文件中的数据做增量恢复,这样恢复速度更快。
四、Zookeeper 客户端(zkCli)的使用
1.多节点类型创建
- 创建持久节点
- 创建持久序列号节点
- 创建临时节点
- 创建临时序号节点
- 创建容器节点
2.查询节点
- 普通查询
- 查询节点相信信息
- cZxid:创建节点的事物 ID
- mZxid:修改节点的事物 ID
- pZxid:添加和删除节点的事物 ID
- ctime:节点的创建时间
- mtime:节点最近修改的时间
- dataVersion:节点内数据的版本,每更新一次数据,版本就会+1
- aclVersion:此节点的权限版本
- ephemeOwner:如果当前节点是临时节点,该值是当前节点所有者的 session id。如果节点不是临时节点,则该值为零。
- dataLength:节点内数据的数据长度
- numChildren:该节点的子节点个数据
3.删除节点
4.权限设置
addauth digest xiaowang:123456
create /test abcd auth:xiaowang:123456:cdwra
- 在另一会话中必须使用账户密码,才能拥有操作该节点的权限
五、Curator 客户端的使用
1.Curator 介绍
Curator 是 Netfix 公司开源的一套 zookeeper 客户端框架,Curator 是对 Zookeeper 支持最好的客户端框架。Curator 封装了大部分 Zookeeper 的功能,比如 Leader 选举,分布式锁,减少了技术人员在使用 Zookeeper 时底层细节开发工作。
2.引入 Curator
<!-- Curator --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.12.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.12.0</version> </dependency> <!-- zookeeper --> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.7.0</version> </dependency>
- application.properties 配置文件
curator.retryCont = 5 curator.elapsedTimeMs = 5000 curator.connectString = 127.0.0.1:2181 curator.sessionTimeoutMs = 60000 curator.connectionTimeoutMs = 5000
WrapperZk.class
package com.qf.bootzkclient.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Data @Component @ConfigurationProperties(prefix = "curator") public class WrapperZk { private int retryCount; private int elapsedTimeMs; private String connectString; private int sessionTimeoutMs; private int connectionTimeoutMs; }
CuratorConfig.class
package com.qf.bootzkclient.config; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryNTimes; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CuratorConfig { @Autowired WrapperZk wrapperZk; @Bean(initMethod = "start") public CuratorFramework curatorFramework() { return CuratorFrameworkFactory.newClient( wrapperZk.getConnectString(), wrapperZk.getSessionTimeoutMs(), wrapperZk.getConnectionTimeoutMs(), new RetryNTimes(wrapperZk.getRetryCount(), wrapperZk.getElapsedTimeMs()) ); } }
3.创建节点
@Slf4j @SpringBootTest class BootZkClientApplicationTest { @Autowired private CuratorFramework curatorFramework; @Test public void create() throws Exception { // 添加持久节点 String path = curatorFramework.create().forPath("/curator-node2"); // 添加临时序号节点 // String s = curatorFramework.create().withProtection(CreateMode.EPHEMERAL_SEQUENTIAL). // forPath("/curator-node", "some-date".getBytes()); System.out.println("curator create node " + path + " successfully"); } }
六、zk 实现分布式锁
1.zk 中锁的种类
- 读锁:大家都可以读取,要想上读锁前提:之前没有写锁。
- 写锁:只有得到写锁的才能写,要想上写锁的前提是,之前没有任何锁。
2.zk 中如何上锁
- 创建一个临时序号节点,节点的数据是 read,表示读锁
- 获取当前 zk 中序号比自己小的所有节点
- 判断最小节点是否是读锁:
- 如果不是读锁,则上锁失败,为最小节点设置监听。阻塞等待,zk 的 watch 机制会当最小节点发生变化时通知当前节点,于是在执行第二步的流程
- 如果是读锁的话,则上锁成功
3.zk 如何上写锁
- 创建一个临时序号节点,节点的数据是 wirte,表示是写锁
- 获取 zk 中所有的子节点
- 判断自己是否是最小节点:
- 如果是,则写锁成功
- 如果不是,说明前面还有锁,则上锁失败,监听最小的节点,如果最小节点发生变化,则回到第二步。
4.羊群效应
如果用上述的上锁方式,只要有节点发生变化,就会触发其他节点的监听事件,这样的话对 zk 的压力非常大——羊群效应。可以调整成链式监听,就可以解决这个问题。
5.Curator 实现读写锁
- 获取读锁
@Test void testGetReadLock() throws Exception { // 读写锁 InterProcessReadWriteLock interProcessReadWriteLock = new InterProcessReadWriteLock(curatorFramework, "/lock1"); // 获取读锁对象 InterProcessLock interProcessLock = interProcessReadWriteLock.readLock(); System.out.println("等待获取读锁对象"); // 获取锁 interProcessLock.acquire(); for (int i = 0; i <= 100; i++) { Thread.sleep(3000); System.out.println(i); } // 释放锁 interProcessLock.release(); System.out.println("等待释放锁"); }
- 获取写锁
@Test void testGetWriteLock() throws Exception { // 读写锁 InterProcessReadWriteLock interProcessReadWriteLock = new InterProcessReadWriteLock(curatorFramework, "/lock1"); // 获取读锁对象 InterProcessLock interProcessLock = interProcessReadWriteLock.writeLock(); System.out.println("等待获取写锁对象"); // 获取锁 interProcessLock.acquire(); for (int i = 0; i <= 100; i++) { Thread.sleep(3000); System.out.println(i); } // 释放锁 interProcessLock.release(); System.out.println("等待释放锁"); }
七 、zk的 watch 机制
1.watch 机制介绍
我们可以吧 Watch 理解成是注册在特定 Znode 上的触发器,当这个 Znode 发生改变,也就是调用了 create, delete, setData 方法的时候,将会触发 Znode 上注册的对应时间,请求 Watch 的客户端会接受到异步通知。
具体交互过程如下:
getDate 方法,watch 是 true 。服务端接到请求,返回节点数据,并且在对应的哈希表插入被 Watch 的 Znode 路径。
- 当被 Watch 的 Znode 已删除,服务器会查找哈希表,找到该 Znode 对应的所有 Watcher,异步通知客户端,并删除花溪表中对应的 Key-Value。
客户端使用了 NIO 通信模式监听服务端的调用。
2.zkCli 客户端使用 watch
create /test xxx # 一次性监听 get -w /test # 监听目录,创建和删除子节点会收到通知。子节点中新增节点不会收到通知 ls -w /test # 对于子节点的变化,但内容的变化不会收到通知 ls -R -w /test
3.curator 客户端使用 watch
@Test public void addNodeListener() throws Exception { NodeCache nodeCache = new NodeCache(curatorFramework, "/curator-node"); nodeCache.getListenable().addListener(new NodeCacheListener() { @Override public void nodeChanged() throws Exception { log.info("{} path nodeChange:","/curator-node"); printNodeDate(); } }); nodeCache.start(); // 让这个线程阻塞在这里避免监控线程关闭 System.in.read(); } public void printNodeDate() throws Exception { byte[] bytes = curatorFramework.getData().forPath("/curator-node"); log.info("data: {}",new String(bytes)); }
八、zookeeper 集群
1.Zookeepr 集群角色
zookeeper 集群中的节点三种角色
- Leader:处理集群的所有事务请求,集群中只有一个 Leader
- Follower:只能处理请求,参与 Leader 选举
- Obeserver:只能处理请求,提升集群读的性能,但不能参与 Leader 选举
2.docker 安装 zookdeeper 集群
- 创建一个
docker-compose.yml
version: '3.7' services: zoo1: image: zookeeper restart: always container_name: zoo1 ports: - "2181:2181" environment: ZOO_MY_ID: 1 ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181 server.4=zoo4:2888:3888;2181:observer zoo2: image: zookeeper restart: always container_name: zoo2 ports: - "2182:2181" environment: ZOO_MY_ID: 2 ZZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181 server.4=zoo4:2888:3888;2181:observer zoo3: image: zookeeper restart: always container_name: zoo3 ports: - "2183:2181" environment: ZOO_MY_ID: 3 ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181 server.4=zoo4:2888:3888;2181:observer zoo4: image: zookeeper restart: always container_name: zoo4 ports: - "2184:2181" environment: ZOO_MY_ID: 4 ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181 server.4=zoo4:2888:3888;2181:observer
- 后台运行
COMPOSE_PROJECT_NAME=zk_test docker-compose up
- 连接
zkClis.sh
- 查看配置
zkServer.sh status # 查询结果 ZooKeeper JMX enabled by default Using config: /conf/zoo.cfg Client port found: 2181. Client address: localhost. Client SSL: false. Mode: leader
九、ZAB 协议
1.什么是 ZAB 协议
zookeeper 作为非常重要的分布式协调组件,需要进行集群部署,集群中会以一株多从多形式进行部署。zookeeper 为了保证数据的一致性,使用了 ZAB(Zookeeper Atomic Broadcas)协议,这个协议解决了 Zookeeper 的崩溃恢复和主从数据同步的问题。
2.ZAB 协议定义的四种节点状态
- Following:Follower 节点(从节点)所处状态。
- Leading:Leader 节点(主节点)所处状态。
3.集群上线时的 Leader 选举过程
Zookeeper 集群中的节点在上线时,将会进入 Looking 状态,也就是选举 Leader的状态,这个状态具体会发生什么?
myid:
zXid:事物 ID,描述数据变化
数据如何变化
4.Zookeeper 中的 NIO 与 BIO 的应用
- NIO
- 用与被客户端连接的2181 端口,使用的是 NIO 模式与客户端建立连接
- 客户端开启 Watch 时,也使用 NIO,等待 Zookeeper 服务等回调
- BIO
- 集群在选举时,多个节点之间等投票通信,使用 BIO 进行通信。
十、CAP 原理
- 一致性(Consistency)
- 可用性(Availability)
- 分区容错性(Partition tolerance)
标签:zookeeper,
zk,
Zookeeper,
curator,
介绍,
2181,
节点
From: https://www.cnblogs.com/SolitaryHua/p/17512846.html