一,zookeeper的名词复盘
1,集群角色
2,数据模型
zookeeper 的视图结构和标准的文件系统非常类似,每一个节点称之为 ZNode, 是 zookeeper 的最小单元。每个 znode上都可以保存数据以及挂载子节点。构成一个层次化的树形结构。
持久节点 PERSISTENT
创建后会一直存在 zookeeper 服务器上,直到主动删除
持久有序节点 PERSISTENT_SEQUENTIAL
每个节点都会为它的一级子节点维护一个顺序
临时节点 EPHEMERAL
临时节点的生命周期和客户端的会话绑定在一起,当客户端会话失效该节点自动清理
临时有序节点 EPHEMERAL
在临时节点的基础上多了一个顺序性
CONTAINER 当子节点都被删除后,Container 也随即删除,PERSISTENT_WITH_TTL 超过 TTL 未被修改,且没有子节点PERSISTENT_SEQUENTIAL_WITH_TTL 客户端断开连接后不会自动删除 Znode,如果该 Znode 没有子 Znode 且在给定 TTL 时间内无修改,该 Znode 将会被删除;TTL 单位是毫秒,必须大于0 且小于或等于 EphemeralType.MAX_TTL
3,会话
-
Client 初始化连接,状态转为 CONNECTING(①)
-
Client 与 Server 成功建立连接,状态转为 CONNECTED(②)
-
Client 丢失了与 Server 的连接或者没有接受到 Server 的响应,状态转为 CONNECTING(③)
-
Client 连上另外的 Server 或连接上了之前的 Server,状态转为 CONNECTED(②)
-
若会话过期(是 Server 负责声明会话过期,而不是 Client ),状态转为 CLOSED(⑤),状态转为 CLOSED
-
Client 也可以主动关闭会话(④),状态转为 CLOSED
4,Stat状态信息
每个节点除了存储数据内容以外,还存储了数据节点本身的一些状态信息,通过 get 命令可以获得状态信息的详细内容。
5,版本-保证分布式数据原子性
zookeeper 为数据节点引入了版本的概念,每个数据节点都有三类版本信息,对数据节点任何更新操作都会引起版本号的变化
版本有点和我们经常使用的乐观锁类似。这里有两个概念说一下,一个是乐观锁,一个是悲观锁
悲观锁:是数据库中一种非常典型且非常严格的并发控制策略。假如一个事务 A 正在对数据进行处理,那么在整个处理过程中,都会将数据处于锁定状态,在这期间其他事务无法对数据进行更新操作。
乐观锁:乐观锁和悲观锁正好想法,它假定多个事务在处理过程中不会彼此影响,因此在事务处理过程中不需要进行加锁处理,如果多个事务对同一数据做更改,那么在更新请求提交之前,每个事务都会首先检查当前事务读取数据后,是否有其他事务对数据进行了修改。如果有修改,则回滚事务再回到 zookeeper,version 属性就是用来实现乐观锁机制的“写入校验”
6,Watcher
zookeeper 提供了分布式数据的发布/订阅功能,zookeeper允许客户端向服务端注册一个 watcher 监听,当服务端的一些指定事件触发了 watcher,那么服务端就会向客户端发送一个事件通知。
值得注意的是,Watcher 通知是一次性的,即一旦触发一次通知后,该 Watcher 就失效了,因此客户端需要反复注册Watcher,即程序中在 process 里面又注册了 Watcher,否则,将无法获取 c3 节点的创建而导致子节点变化的事件。
二,Zookeeper基于 Java 访问
针对 zookeeper,比较常用的 Java 客户端有 zkclient、curator。由于 Curator 对于 zookeeper 的抽象层次比较高,简化了zookeeper 客户端的开发量。使得 curator 逐步被广泛应用。
- 封装 zookeeper client 与 zookeeper server 之间的连接处理
- 提供了一套 fluent 风格的操作 api
- 提供 zookeeper 各种应用场景(共享锁、leader 选举)的抽象封装
1,依赖jar包
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.1</version>
</dependency>
2,建立连接
private static final String CONNECTION_STR = ",";
CuratorFramework curatorFramework = CuratorFrameworkFactory
.builder()
.connectString(CONNECTION_STR)
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
curatorFramework.start();
重试策略:Curator 内部实现的几种重试策略:
• ExponentialBackoffRetry:重试指定的次数, 且每一次重试之间停顿的时间逐渐增加.
• RetryNTimes:指定最大重试次数的重试策略
• RetryOneTime:仅重试一次
• RetryUntilElapsed:一直重试直到达到规定的时间
namespace: 值得注意的是 session2 会话含有隔离命名空间,即客户端对 Zookeeper 上数据节点的任何操作都是相对/curator目录进行的,这有利于实现不同的 Zookeeper 的业务之间的隔离
3,节点的增删改查
//增
curatorFramework
.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)
.inBackground()
.forPath("/data/program", "test".getBytes());
//删
curatorFramework
.setData()
.forPath("/data/program", "update".getBytes());
//改
curatorFramework.delete()
.withVersion(0)
.forPath("/data/program");
//查
curatorFramework.getData().forPath("/data/program");
4,节点权限设置
Zookeeper 作为一个分布式协调框架,内部存储了一些分布式系统运行时的状态的数据,比如 master 选举、比如分布式锁。对这些数据的操作会直接影响到分布式系统的运行状态。因此,为了保证 zookeeper 中的数据的安全性,避免误操作带来的影响。Zookeeper 提供了一套 ACL 权限控制机制来保证数据的安全。
对于删除操作,即使设置了权限也没有啥用,照样能删除。
1)权限控制代码
//权限
List<ACL> acls = new ArrayList<>();
/**
* ADMIN 16
* ALL 31
* CREATE 4
* DELETE 8
* READ 1
* WRITE 2
* ==========================
* public Id(String scheme, String id) {
* this.scheme = scheme;
* this.id = id;
* }
*
*/
ACL acl = new ACL(ZooDefs.Perms.READ | ZooDefs.Perms.WRITE
, new Id("digest"
, DigestAuthenticationProvider.generateDigest("admin:admin")));
acls.add(acl);
curatorFramework.create().withMode(CreateMode.PERSISTENT).withACL(acls).forPath("/auth");
2)权限模式
Ip 通 过 ip 地 址 粒 度 来 进 行 权 限 控 制 , 例 如 配 置[ip:192.168.0.1], 或者按照网段 ip:192.168.0.1/24 ;
Digest:最常用的控制模式,类似于 username:password ;设 置 的 时 候 需 要DigestAuthenticationProvider.generateDigest() SHA- 加 密和 base64 编码
World: 最开放的控制模式,这种权限控制几乎没有任何作用,数据的访问权限对所有用户开放。 world:anyone
Super: 超级用户,可以对节点做任何操作
3)授权对象
指权限赋予的用户或一个指定的实体,不同的权限模式下,授权对象不同
Id ipId1 = new Id("ip", "192.168.190.1");
Id ANYONE_ID_UNSAFE = new Id("world", "anyone");
4)权限
指通过权限检查后可以被允许的操作,create /delete/read/write/admin
Create 允许对子节点 Create 操作
Read 允许对本节点 GetChildren 和 GetData 操作
Write 允许对本节点 SetData 操作
Delete 允许对子节点 Delete 操作
Admin 允许对本节点 setAcl 操作
5,节点事件监听
Watcher 监听机制是 Zookeeper 中非常重要的特性,我们基于zookeeper 上创建的节点,可以对这些节点绑定监听事件,比如可以监听节点数据变更、节点删除、子节点状态变更等事件,通过这个事件机制,可以基于 zookeeper 实现分布式锁、集群管理等功能。
zookeeper 事件 | 事件含义 |
---|---|
EventType.NodeCreated | 当 node-x 这个节点被创建时,该事件被触发 |
EventType.NodeChildrenChanged | 当 node-x 这个节点的直接子节点被创建、被删除 |
EventType.NodeDataChanged | 当 node-x 这个节点的数据发生变更时,该事件被触发。 |
EventType.NodeDeleted | 当 node-x 这个节点被删除时,该事件被触发。 |
EventType.None | 当 zookeeper 客户端的连接状态发生变更时 |
watcher 机制有一个特性:当数据发生改变的时候,那么zookeeper 会产生一个 watch 事件并发送到客户端,但是客户端只会收到一次这样的通知,如果以后这个数据再发生变化,那么之前设置 watch 的客户端不会再次收到消息。因为他是一次性的;如果要实现永久监听,可以通过循环注册来实现
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.1</version>
</dependency>
Curator 提供了三种 Watcher 来监听节点的变化
• PathChildCache:监视一个路径下孩子结点的创建、删除、更新。
• NodeCache:监视当前结点的创建、更新、删除,并将结点的数据缓存在本地。
• TreeCache:PathChildCache 和 NodeCache 的“合体”,监视路径下的创建、更新、删除事件,并缓存路径下所有孩子结点的数据。
//Watch 创建,修改,删除
@SneakyThrows
private static void addListenerWithNode(CuratorFramework curatorFramework) {
/**
* PathChildCache 针对于子节点的创建,删除和更新 触发事件
* NodeCache 针对当前节点的变化触发事件
* TreeCache 综合事件
*/
NodeCache nodeCache = new NodeCache(curatorFramework, "/watch", false);
NodeCacheListener listener = () -> {
System.out.println("received Node Changed");
System.out.println(nodeCache.getCurrentData().getPath() + "/" + new String(nodeCache.getCurrentData().getData()));
};
nodeCache.getListenable().addListener(listener);
nodeCache.start();
}
//实现服务注册中心的时候,可以针对服务做动态感知
@SneakyThrows
private static void addListenerWithChild(CuratorFramework curatorFramework) {
PathChildrenCache nodeCache = new PathChildrenCache(curatorFramework, "/watch", true);
PathChildrenCacheListener listener = (framework, pathChildrenCacheEvent)
-> System.out.println(pathChildrenCacheEvent.getType() + "->" + pathChildrenCacheEvent.getData().getData());
nodeCache.getListenable().addListener(listener);
nodeCache.start(PathChildrenCache.StartMode.NORMAL);
}