在之前的文章中ZK集群搭建,中已经将ZK的集群环境搭建好.接下来,我们需要对集群中的一些相关概念做一些解释,以便在之后更好的学习.
集群角色
在ZK当中总共有三种角色,分别是leader,follower,observer.
leader:服务器是整个 zookeeper 集群的核心.主要的工作任务有两项:
1.事物请求的唯一调度和处理者,保证集群事物处理的顺序性集群内部各服务器的调度者
2.集群内部各服务器的调度者
follower:
-
处理客户端非事物请求、转发事物请求给 leader 服务器
-
参与事物请求 Proposal 的投票(需要半数以上服务器通过才能通知 leader commit 数据;
Leader 发起的提案,要求 Follower 投票)
-
参与 Leader 选举的投票
observer是 zookeeper3.3 开始引入的一个全新的服务器角色,从字面来理解,该角色充当
了观察者的角色。其功能基本和follower基本一致,唯一和follower不同的是,该节点不参与投票,包括Proposal 的投票和leader的选举.observer节点是提供非事务请求服务,通常在不影响集群事务处理能力的前提下提升集群非事务处理的能力.
ZK数据模型
zk的数据结构和标准的文件系统非常类似.每一个节点称之为ZNode,这个是ZK中最小的单元.每个znode都可以保存数据以及挂载子节点.形成一个层次化的数据结构.
节点可分为以下几类:
持久节点:创建之后会一直保存在zk服务器上,直到自己主动调用delete命令删除,
持久有序节点:在持久节点的基础上增加了有序性.每个节点都会为它的一级子节点维护一个顺序
临时节点:临时节点的生命周期和客户端会话绑定在一起,当客户端会话关闭或者失去心跳连接,节点自动删除.
临时有序结点:在临时节点基础上增加了有序性.
在3.5.5的版本中新增加了以个容器节点(CONTAINER) .
CONTAINER:容器节点,用于Leader、Lock等特殊用途,当容器节点不存在任何子节点时,容器将成为服务器在将来某个时候删除的候选节点。
PERSISTENT_WITH_TTL:带TTL(time-to-live,存活时间)的永久节点,节点在TTL时间之内没有得到更新并且没有孩子节点,就会被自动删除。
PERSISTENT_SEQUENTIAL_WITH_TTL:带TTL(time-to-live,存活时间)和单调递增序号的永久节点,节点在TTL时间之内没有得到更新并且没有孩子节点,就会被自动删除。
### 会话
客户端与服务器上的一个连接被称之为一次会话.会话有以下几种状态:
[外链图片转存失败(img-WiX9E8b8-1563002388736)(C:\Users\39365\AppData\Roaming\Typora\typora-user-images\1562982577412.png)]
- Client 初始化连接,状态转为 CONNECTING(①)
- Client 与 Server 成功建立连接,状态转为 CONNECTED(②)
- Client 丢失了与 Server 的连接或者没有接受到 Server 的响 应,状态转为 CONNECTING(③)
- Client 连上另外的 Server 或连接上了之前的 Server,状态 转为 CONNECTED(②)
- 若会话过期(是 Server 负责声明会话过期,而不是 Client ), 状态转为 CLOSED(⑤),状态转为 CLOSED
- Client 也可以主动关闭会话(④),状态转为 CLOSED
Stat 状态信息
每个节点除了存储数据内容以外,还存储了数据节点本身的 一些状态信息,通过 get 命令可以获得状态信息的详细内容
[外链图片转存失败(img-43fYdKSr-1563002388737)(C:\Users\39365\AppData\Roaming\Typora\typora-user-images\1562983008534.png)]
Watcher机制
zookeeper 提供了分布式数据的发布/订阅功能,zookeeper 允许客户端向服务端注册一个 watcher 监听,当服务端的一 些指定事件触发了 watcher,那么服务端就会向客户端发送一个事件通知。
值得注意的是,Watcher 通知是一次性的,即一旦触发一次通知后,该 Watcher 就失效了,因此客户端需要反复注册 Watcher.
在了了解完ZK的一些相关概念之后,接下来我们就要进入真正的实操环节了.使用java来对zk进行相关的crud的操作.针对任何一个这样的中间接来说,都会提供相应的客户端组件对其进行操作.针对zk,比较常用的java客户端有zkclient、curator。由于curator对zk的抽象层次比较高了,减缓了zk客户端的开发,使得curator影响比较广泛.
1. 封装 zookeeper client 与 zookeeper server 之间的连接处理
- 提供了一套 fluent 风格的操作 api
- 提供 zookeeper 各种应用场景(共享锁、leader 选举)的抽象 封装
要使用curator就需要在pom中引入相应的jar包
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.0</version>
</dependency>
建立连接
CuratorFramework curatorFramework = CuratorFrameworkFactory.builder().
connectString(CONNECTION_STR).sessionTimeoutMs(5000).
retryPolicy(new
ExponentialBackoffRetry(1000, 3)).namespace("curator").build();
curatorFramework.start();
retryPolicy指的重试策略,在Curator 内部实现的几种重试策略:
- ExponentialBackoffRetry:重试指定的次数, 且每一次重试之间停顿的时间逐渐增加.
- RetryNTimes:指定最大重试次数的重试策略
- RetryOneTime:仅重试一次
- RetryUntilElapsed:一直重试直到达到规定的时间
namespace: 值得注意的是 session 会话含有隔离命名空间,即 客户端对 Zookeeper 上数据节点的任何操作都是相对/curator 目录进行的,这有利于实现不同的 Zookeeper 的业务之间的隔离.
Curator 中的api写的都很简单明了,稍后可看上传的代码示例.
权限模式
在zk节点中还有一个很重要的概念就是ACL权限.当觉得数据较为重要,切需要设置相关访问权限时,可设置该配置.
给节点赋权
List<ACL> list = new ArrayList<>();
ACL acl = new ACL(ZooDefs.Perms.READ | ZooDefs.Perms.WRITE,
new Id("digest", DigestAuthenticationProvider.generateDigest("admin:admin")));
ACL acl2 = new ACL(ZooDefs.Perms.READ,
new Id("digest", DigestAuthenticationProvider.generateDigest("admin2:admin2")));
list.add(acl);
list.add(acl2);
curatorFramework.create().withMode(CreateMode.PERSISTENT).withACL(list).forPath("/auth", "sc".getBytes());
访问授权的节点
CuratorFramework curatorFramework = CuratorFrameworkFactory.builder().
connectString(CONNECTION_STR).sessionTimeoutMs(5000).
authorization("digest", "admin:admin".getBytes()).
retryPolicy(new ExponentialBackoffRetry(1000, 3)).namespace("curator").build();
修改已经存在节点的权限
curatorFramework.setACL().withACL().forPath("/aaaa");
授权的模式主要分为三种:
-
Ip 通 过 ip 地 址 粒 度 来 进 行 权 限 控 制 , 例 如 配 置[ip:192.168.0.1], 或者按照网段 ip:192.168.0.1/24 ; Id ipId1 = new Id(“ip”, “192.168.190.1”);
-
Digest:最常用的控制模式,类似于 username:password ;设 置 的 时 候 需 要DigestAuthenticationProvider.generateDigest() SHA- 加 密和 base64 编码
-
World: 最开放的控制模式,这种权限控制几乎没有任何作用,数据的访问权限对所有用户开放。 world:anyone Id ANYONE_ID_UNSAFE = new Id(“world”, “anyone”);
-
Super: 超级用户,可以对节点做任何操作
[外链图片转存失败(img-zHfItRhb-1563002388738)(C:\Users\39365\AppData\Roaming\Typora\typora-user-images\1562984366529.png)]
节点监听事件
Watcher 监听机制是 Zookeeper 中非常重要的特性,我们基于 zookeeper 上创建的节点,可以对这些节点绑定监听事件,比如可以监听节点数据变更、节点删除、子节点状态变更等 事件,通过这个事件机制,可以基于 zookeeper 实现分布式 锁、集群管理等功能
[外链图片转存失败(img-gBRVPB97-1563002388738)(C:\Users\39365\AppData\Roaming\Typora\typora-user-images\1562984483227.png)]
watcher 机制有一个特性:当数据发生改变的时候,那么 zookeeper 会产生一个 watch 事件并发送到客户端,但是客 户端只会收到一次这样的通知,如果以后这个数据再发生变 化,那么之前设置 watch 的客户端不会再次收到消息。因为 他是一次性的;如果要实现永久监听,可以通过循环注册来 实现
要使用curator 事件监听的引入依赖
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.0</version>
</dependency>
Curator 提供了三种 Watcher 来监听节点的变化
• PathChildCache:监视一个路径下孩子结点的创建、删 除、更新。
PathChildrenCache pathChildrenCache = new PathChildrenCache(curatorFramework,"/watch",true);PathChildrenCacheListener pathChildrenCacheListener = (curatorFramework1, pathChildrenCacheEvent) -> { System.out.println(pathChildrenCacheEvent.getType()+"->"+new String(pathChildrenCacheEvent.getData().getData()));};pathChildrenCache.getListenable().addListener(pathChildrenCacheListener);pathChildrenCache.start();
• NodeCache:监视当前结点的创建、更新、删除,并将 结点的数据缓存在本地。
NodeCache nodeCache = new NodeCache(curatorFramework, "/watch", false);
NodeCacheListener nodeCacheListener = () -> {
System.out.println("receive Node Changed");
System.out.println(nodeCache.getCurrentData().getPath()+"---"+new String(nodeCache.getCurrentData().getData()));
};
nodeCache.getListenable().addListener(nodeCacheListener);
nodeCache.start();
• TreeCache:PathChildCache 和 NodeCache 的“合体”, 监视路径下的创建、更新、删除事件,并缓存路径下所 有孩子结点的数据。