1. ZooKeeper数据模型
1.1 ZooKeeper数据节点
ZooKeeper的数据模型是一颗树结构,每一个树节点是一个数据节点,我们称它为ZNode。
而每一个ZNode的节点路径标识使用
斜杠/
作为分隔符,我们可以在ZNode节点下写入数据、创建节点,这种
斜杠/
作为路径分隔符的方式和Unix文件系统路径非常相似。
大家可以看下Unix文件系统路径,以
斜杠/
作为路径分隔符。
# 根目录
/
# 可执行文件所在位置
/bin
# 设备驱动
/dev
这是ZooKeeper数据模型概念图,是不是非常类似呢?
另外ZooKepper这种
斜杠/
作为路径分隔符正好和Windows相反,Windows使用的是
反斜杠\
。
# Windows路径示例
C:\Java\jdk1.8.0_311
1.2 数据节点类型
ZooKeeper一共有四种节点类型,但从整体来看主要是持久节点类型、临时节点类型这两种,另外两种类型只是在以上两种节点类型基础上增加了顺序的特性。大家这样理解会更方便记忆~
- 持久节点:这种数据节点一旦背创建后,就会一直存在于ZooKeeper服务器上,除非对该数据节点执行删除操作。
- 持久顺序节点:刚刚和大家说了,该节点类型就是在持久节点基础上增加了顺序的特性。如果在持久顺序节点类型的父节点创建子节点,ZooKeeper会为该子节点名加上一个数字后缀来维护子节点的顺序。
- 临时节点:临时节点比较特殊,它的生命周期是和客户端会话绑定在一起的。这个客户端可以是连接ZooKeeper的某一个终端命令窗口,也可以是连接ZooKeeper的某一个Spring服务线程。如果客户端会话失效了,那这个临时节点就会被自动清除。
- 临时顺序节点:在临时节点的基础上添加了顺序特性。
另外大家记住一点,临时节点只能作为叶子节点,是不能在临时节点下面创建任何子节点的。原因大概是临时节点子节点没有存在的意义,创建子节点的场景大多是基于持久节点的场景,这种设计也可以防止对临时节点的误用。
1.3 数据节点的版本
ZooKeeper数据节点的版本概念和CAS操作的版本概念是一样的,同样是在多线程环境下,通过乐观锁这种无锁操作来保证线程安全性。
当一个数据节点被创建后,该节点的version值为0,代表它被更新过0次,如果后续对该节点进行更新操作,那version便会递增。
对数据节点的每次更新,都会对比数据节点的version是否是预期值,只有符合预期值,才会将新值更新到该数据节点。
大家可以通过以下CAS例子代码来了解CAS操作的执行过程。
@Slf4j
public class CAS implements Runnable {
private static AtomicInteger val = new AtomicInteger(0);
private void compareSwap(int expectVal, int operateVal) {
if (val.compareAndSet(expectVal, operateVal)) {
log.info("the thread called {} operated", Thread.currentThread().getName());
} else {
this.run(); // 相当于获取不到锁则重新执行
}
}
@Override
public void run() {
int curVal = val.get();
log.info("the thread called {} get val is {}", Thread.currentThread().getName(), curVal);
compareSwap(curVal, curVal + 1);
}
public static void main(String[] args) throws InterruptedException {
CAS cas0 = new CAS();
CAS cas1 = new CAS();
Thread thread0 = new Thread(cas0, "cas0");
Thread thread1 = new Thread(cas1, "cas1");
thread0.start();
thread1.start();
thread0.join();
thread1.join();
log.info("current thread name: {} , the value of val: {}", Thread.currentThread().getName(), val);
}
}
控制台打印:
[ INFO ]the thread called cas0 get val is 0
[ INFO ]the thread called cas1 get val is 0
[ INFO ]the thread called cas0 operated
[ INFO ]the thread called cas1 get val is 1
[ INFO ]the thread called cas1 operated
[ INFO ]current thread name: main , the value of val: 2
1.4 事务ID
我们熟悉的数据库事务一般是包含对数据库状态的读写操作,数据库事务具有ACID特性:原子性、一致性、持久性、隔离性。数据库事务这块大家有不懂的可以看我的往期文章。
但ZooKeeper的事务和数据库事务大相径庭。ZooKeeper事务一般是包括对数据节点的创建、删除、更新,也包括客户端会话创建、失效情况对临时节点的影响。
每一个事务请求,ZooKeeper都会为其分配一个全局唯一的事务ID,我们称它为ZXID。所以ZXID也可以间接反映出对数据节点操作的全局顺序,这个全局顺序在Follower服务器对Leader服务器的数据复制上相当重要,可以用来保证数据的一致性。
2. Watcher机制
ZooKeeper拥有分布式通知的功能,这个功能是基于Watcher机制来实现的。一个Watcher对象就像一个订阅者,当订阅的主题状态发生变化,就会通知Watcher订阅者作出一定动作。
Watcher机制的工作流程,首先是客户端向ZooKeeper服务器注册Watcher通知,接着会将Watcher对象存储在客户端本身的WatchManager中。当ZooKeeper服务器触发Watcher事件后会向客户端发起通知,客户端就从本身的WatchManager取出对应的Watcher对象来执行回调操作。
Watcher机制的大致流程大家可以参考下图:
3. AI生成ZooKeeper面试题
1. 什么是 ZooKeeper?它的主要作用是什么?
** 答:ZooKeeper 是一个开源的分布式协调服务,旨在解决分布式系统中的协调问题。它通过提供简单的原语,如命名服务、配置管理、分布式锁和分布式消息队列等,帮助管理和维护分布式应用中的元数据。ZooKeeper 保证了高可用性、一致性和可靠性,支持分布式系统中的多个节点共同工作。2. **ZooKeeper 的典型应用场景有哪些?
答:ZooKeeper 的典型应用场景包括:
- 分布式锁:提供锁服务,用于协调多个节点对资源的并发访问。
- 服务发现:在分布式环境下,帮助客户端找到可用的服务。
- 分布式协调:通过 ZooKeeper 管理元数据,实现不同节点间的同步和协调。
- 配置管理:集中管理分布式系统中的配置信息,节点在启动时读取或更新配置。
- 领导者选举:在多个节点中自动选举一个主节点,以保证系统的一致性和高效性。
3. ZooKeeper 中的“节点”是什么?有哪几种类型的节点?
答:ZooKeeper 的节点称为 ZNode,是 ZooKeeper 数据模型的基本单元。ZNode 类似于文件系统中的目录,可以存储数据,也可以拥有子节点。ZooKeeper 有两种类型的 ZNode:
- 持久节点(Persistent ZNode):当客户端断开连接后,持久节点仍然存在,除非手动删除。
- 临时节点(Ephemeral ZNode):当创建该节点的客户端会话断开时,临时节点会自动删除。临时节点不能有子节点。
- 另外,还有 顺序节点,即 ZooKeeper 自动为节点名添加一个递增的序号,用于有序访问和锁实现。
4. ZooKeeper 如何保证一致性?
答:ZooKeeper 通过ZAB 协议(ZooKeeper Atomic Broadcast) 来保证数据一致性。ZAB 是一种分布式一致性协议,类似于 Paxos。它通过以下机制确保一致性:
- Leader 负责写操作:所有写操作必须经过 Leader,Leader 决定数据的写入顺序,并向其他 Follower 节点广播。
- 过半投票机制:Leader 在提交写操作之前,必须获得集群中半数以上节点的确认,以保证数据的强一致性。
- 保证 FIFO 顺序:ZooKeeper 保证同一客户端发出的请求按顺序执行,确保操作的线性一致性。
5. ZooKeeper 中的“会话”是什么?会话失效如何处理?
** 答:会话是 ZooKeeper 客户端和服务器之间的连接。在会话期间,客户端可以执行读写操作。ZooKeeper 通过心跳机制检测会话的有效性。如果客户端长时间没有发送心跳信号,ZooKeeper 会认为该会话失效,删除与该会话相关的临时节点。客户端可以指定会话的超时时间,超时会导致会话失效。6. **ZooKeeper 中的“临时节点”有什么特性?
答:临时节点具有以下特性:
- 自动删除:当创建该节点的客户端断开连接或会话失效时,ZooKeeper 自动删除该节点。
- 不允许子节点:临时节点不能拥有子节点,这是为了避免由于客户端突然断开连接而导致的部分子节点不一致性。
- 常用于分布式锁和心跳检测:临时节点通常用于实现分布式锁,或者存储某个客户端的心跳信息,用于服务的健康监测。
7. 什么是 ZooKeeper 的“监听机制”?
答:ZooKeeper 的监听机制允许客户端对某个节点的变化(如节点的创建、删除、数据修改等)进行监听。具体工作方式:
- 注册监听器:客户端对某个 ZNode 注册监听器,并指定要监听的事件类型。
- 触发事件:当该 ZNode 的状态发生变化时(如节点数据更新或节点删除),ZooKeeper 会通知注册了该事件的客户端。
- 重新监听:一次性触发后,客户端如果还要继续监听同一事件,必须重新注册监听器。
8. ZooKeeper 如何进行 Leader 选举?
答:ZooKeeper 使用
ZAB 协议
实现 Leader 选举。当 ZooKeeper 集群启动时,所有节点都会进行投票选举 Leader。投票过程如下:
- 每个节点投票:每个节点将自身标记为候选人,并将投票信息广播给其他节点。
- 选票收集:节点收到其他节点的投票信息后,会判断投票结果,选择拥有最高 zxid(事务 ID)的节点作为 Leader。
- 过半确认:一旦某个节点获得了超过半数的支持,它就成为 Leader,其他节点成为 Follower。 Leader 选举是 ZooKeeper 保证分布式一致性的重要机制。
9. ZooKeeper 的 CAP 定理中,符合哪两个特性?
答:根据 CAP 定理,分布式系统中只能同时满足一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)中的两个特性。ZooKeeper 属于CP
系统:
- 一致性(Consistency):ZooKeeper 通过 ZAB 协议保证数据在集群中的强一致性。
- 分区容忍性(Partition Tolerance):ZooKeeper 能够在网络分区时保持一致性,但会牺牲部分可用性(例如在 Leader 选举期间,系统短暂不可用)。
10. ZooKeeper 的“会话超时”是什么?如何配置?
答:会话超时是指 ZooKeeper 客户端与服务器之间的连接在超过设定的超时时间没有心跳时,ZooKeeper 会认为客户端掉线并关闭该会话。此时与该会话相关的临时节点也会被删除。
- 会话超时时间可以在客户端创建连接时配置。通过
createSession
方法,可以设置一个合理的超时时间,ZooKeeper 将根据客户端设定的时间维护心跳机制。
11. ZooKeeper 中的“集群模式”是如何工作的?
答:ZooKeeper 的集群模式由多台服务器组成,通常是一个由奇数个节点组成的集群。集群中的节点有三种角色:
Leader、Follower和Observer。
- Leader:处理所有写操作,协调集群中的事务提交。
- Follower:接收并处理客户端的读请求,并将写请求转发给 Leader。
- Observer:只接收读请求,不参与写操作投票,通常用于扩展读性能。
12. ZooKeeper 如何处理写请求和读请求?
答:ZooKeeper 对读写操作有不同的处理方式:
- 写请求:所有的写请求必须由 Leader 处理。Leader 在接收到写请求后,会将数据变更广播给 Follower,并等待超过半数的 Follower 确认后才提交该写操作。这确保了写操作的一致性。
- 读请求:读请求可以由 任何节点 处理(Leader 或 Follower)。由于数据是同步的,读请求不需要等待一致性确认,因此可以快速返回结果。
13. ZooKeeper 中的“过半机制”是什么?为什么重要?
答:ZooKeeper 采用过半机制,即
Quorum
机制,保证集群在至少有半数节点存活的情况下,仍能正常提供服务。
具体来说,一个 ZooKeeper 集群通常由 N 个节点组成,集群至少需要 N/2 + 1 个节点在线才能进行写操作。这样设计是为了保证数据的强一致性和系统的容错能力。
**14. **什么是 ZooKeeper 的“FIFO” 保证?
** 答**:ZooKeeper 保证 FIFO(First In, First Out) 顺序,确保同一客户端发出的请求按顺序执行。这意味着如果客户端依次发出多个请求,ZooKeeper 会按顺序处理这些请求,从而保证操作的线性一致性。这在分布式环境下非常重要,尤其在实现分布式锁或同步操作时。
**15. **ZooKeeper 是如何实现“分布式锁”的?
答:ZooKeeper 实现分布式锁的常见方法是使用临时有序节点。具体步骤如下:
- 每个客户端在锁目录下创建一个临时有序节点。
- ZooKeeper 会为每个节点分配一个递增的序号。
- 客户端通过检查自己创建的节点是否是当前目录下最小的节点,来判断是否获得锁。
- 如果是最小的节点,则该客户端获得锁。如果不是,则监听比自己序号小的节点,当该节点删除时,重新判断是否获得锁。
- 当客户端释放锁时,删除自己创建的节点,其他节点会接到通知,继续竞争锁。
结束啦,希望大家能有所成!!!
你好,我是胡广。 致力于为帮助兄弟们的学习方式、面试困难、入职经验少走弯路而写博客 🌹🌹🌹 坚持每天两篇高质量文章输出,加油!!!🤩
如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 😄 (^ ~ ^) 。想看更多 那就点个关注 吧 我会尽力带来有趣的内容 。
😎感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以 给我留言咨询,希望帮助更多的人
更多专栏:
📊 Java设计模式宝典:从入门到精通(持续更新)📝 Java基础知识:GoGoGo(持续更新)
⚽ Java面试宝典:从入门到精通(持续更新)
🌟 程序员的那些事~(乐一乐)
🤩 Redis知识、及面试(持续更新)
🚀 Kafka知识文章专栏(持续更新)
🎨 Nginx知识讲解专栏(持续更新)
📡 未完待续。。。
🎯 未完待续。。。
🔍 未完待续。。。
感谢订阅专栏 三连文章
版权归原作者 程序员-杨胡广 所有, 如有侵权,请联系我们删除。