ZooKeeper:五种经典应用场景

1 Distributed Configuration 配置管理

我们在项目开发维护过程中会有很多公共变量或资源,需要统一管理,以前可以把它们写在程序公共类或者配置文件中,可是这样以后有变动,程序就需要重新部署,很是不方便,而且分布式、微服务等技术出现,修改维护多个项目管理也变得复杂。

为了解决以上问题,实现一次打包多地部署需求,减少项目管理及安全风险,我们需要将可变变量外移并通过页面统一可视化管理,基于此,很多大厂技术团队都建设了配置中心。

下图是配置中心的通用设计:

ZooKeeper 天然具备作为配置中心的特性,表现在如下两点:

1、配置数据存储到 ZooKeeper 的节点中 ;

2、应用注册改该节点的 watch 监听,如果一旦这个节点数据发生变更,那么所有订阅该节点的客户端都可以获取数据变更通知。

1、发布者作为 Zookeeper 客户端首先在 zk 中创建一个节点,该节点的数据内容即为当前应用服务 A 的配置文件。

2、应用服务 A 在启动时从 zk 的节点上读取数据内容,即获取配置文件内容。

3、读取数据内容后,应用服务 A 会向 zk 的该节点注册一个数据内容变更的 watcher 监听。

4、当发布者更新配置内容并将其更新到 zk 的对应节点数据内容上时,zk 会触发相应的 watcher 事件,并向每一个应用 A 推送此事件通知。

5、应用 A 接收到 watcher 事件后,会触发本地的 watcher 回调方法,该方法将从 zk 中重新拉取节点的数据内容,即获取更新后的配置内容。

2 Service Discovery 服务发现

大规模服务化之后,服务越来越多,服务消费者在调用服务提供者的服务时,需要在配置文件中维护服务提供者的URL地址,当服务提供者出现故障或者动态扩容时,所有相关的服务消费者都需要更新本地配置的URL地址,维护程本很高。

这时,实现服务的上下线动态感知及服务地址的动态维护就显得非常重要了。

上图是我们熟知的 RPC 框架 Dubbo 的架构 ,服务生产者启动之后,会将服务注册到注册中心 ,服务消费者会通过注册中心订阅相关服务, 服务消费者获取到服务路由信息后,调用服务生产者提供的服务。

zookeeper 提供了一种针对 Znode 的订阅/通知机制,就是当 Znode 节点状态发生变化或者 zookeeper 客户端连接状态发生变化时,会触发事件通知;这个机制在服务注册与发现中,对服务调用者及时感知服务提供者的变化提供了非常好的解决方案。

流程:

  • 提供者启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址。
  • 服务消费者启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址
  • 监控中心启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址。

3 Master / Slave 解决单点故障问题

分布式架构中, 为了保证服务的可用性,通常会采用集群模式:其中一个机器宕机后,集群中的其他节点会接替故障节点继续工作。

这种场景中,需要从集群中选择一个节点作为 Master 节点,剩余的其他节点作为备份节点随时待命;当原有的 Master 节点出现故障之后,还需要从备份节点中选择一个节点作为 Master 节点继续服务。

实现 Master选举 有两种方式:

01 争抢临时节点

使用 zk 的 master 选举是利用了 zk 中多个客户端对同一节点创建时,只有一个客户端可以成功的特性实现。

具体来说,由三步完成:

1、多个客户端同时发起对同一临时节点 /master-election/master 进行创建的请求,最终只能有一个客户端成功。这个成功的客户端主机就是 Master,其它客户端就是 Slave。

2、 Slave 都向这个临时节点的父节点 /master-election 注册一个子节点列表的 watcher 监听。

3、一旦该 Master 宕机,临时节点就会消失,zk 服务器就会向所有 Slave 发送子节点变更事件,Slave 在接收到事件后会调用相应的回调方法,该回调方法会重新向这个父节点创建相应的临时子节点。谁创建成功,谁就是新的 Master。

02 创建临时有序节点

在设计 ZooKeeper 实现的 Master 选举机制时,考虑到当无法实现完全的 Active-Active 模式(即所有节点同时处理读写请求)的情况下,可以采用以下策略:

1、父节点类型为 Persistent: 创建一个持久化的父节点作为服务的基础路径。这个父节点不会因为创建它的客户端会话的结束而被删除,确保了即使创建者离线,其他参与者仍然能够找到并使用该节点。

2、子节点类型为 Ephemeral + Sequential: 每个参与选举的服务实例在其启动时,在上述持久化父节点下创建一个临时且顺序的子节点。这些临时节点的存在依赖于创建它们的客户端会话;如果客户端会话终止,相应的节点将自动被移除。顺序特性则赋予每个新创建的节点一个唯一的递增编号,这有助于确定哪个节点是最早的参与者。

3、Master 选择逻辑: 在所有的子节点中,序列号最小的那个节点所对应的客户端会被选为 Master,其余的客户端则被视为 Slave。

4、Slave 监控机制: 每个 Slave 客户端都会监控比自己序列号小的最大子节点的 NodeDeleted 事件。这意味着每一个 Slave 都在等待前一位(按创建时间排序)的 Slave 或 Master 的失效信号。

5、Master 晋升规则: 当某个 Slave 接收到它正在监听的节点被删除的通知(即 NodeDeleted 事件触发),它会检查是否有更早创建的节点仍然存在。如果没有,则该 Slave 自动晋升为新的 Master,承担起协调者的职责。如果有更早的节点存在,则更新监听目标至下一个较早的节点。

4 Sequence Generation 序列产生器

核心流程如下:

1、创建持久节点(persistent node)ZooKeeper 中创建一个持久节点。这个节点用来保存序列号的信息。

2、设置数据版本号为 -1 ,告诉 ZooKeeper 不要进行版本号校验,客户端总会调用成功,ZooKeeper 会将该节点的数据版本号自动 + 1 。

3、客户端会得到最新的数据版本号,作为序列号使用。由于 ZooKeeper 的版本号是递增的,所以每次调用都会得到一个新的唯一序列号。

该方案的优点是一次调用即可分配序列号,缺点是受限于数据版本号,只能分配 32 位序列号。

伪代码如下:

5 Distributed Locking Service 分布式锁

分布式锁是控制分布式系统同步访问共享资源的一种方式。ZooKeeper 可以实现分布式锁的功能。

核心流程如下:

1、创建持久节点(Persistent Node)作为锁对象:

首先,在 ZooKeeper 中为每个锁创建一个持久的(persistent)节点。这相当于给每种类型的锁分配一个唯一的路径。

2、客户端申请锁:

客户端为了获取锁,会在该锁对应的持久节点下创建一个临时顺序(ephemeral and sequential)子节点。例如,如果锁对象节点是 /Locks/Lock1/Lock,那么客户端可能会创建类似 /locks/Lock1/Lock0000000001 的节点。

3、检查序列号:

创建完子节点后,客户端会获取锁对象节点下的所有子节点列表,并按序号排序。

如果客户端发现它所创建的子节点拥有最小的序列号,那么它就获得了锁。

4、释放锁:

拥有锁的客户端在完成任务后可以通过删除自己的子节点来释放锁。由于节点是临时的(ephemeral),所以即使客户端崩溃,ZooKeeper 也会自动删除该节点,从而避免死锁。

5、等待锁:

如果客户端没有获得锁(即它的子节点不是序列号最小的),它应该监听比自己序列号小的最近一个子节点的 NodeDeleted 事件。

一旦这个更早的子节点被删除(意味着持有锁的客户端已经完成了工作并释放了锁),当前客户端将收到通知,然后重新检查它是否现在可以获取锁。

相关推荐
ZIXEL子虔科技1 天前
重绘赛道:AI将如何定义国产CAD的下一代?
ai·云原生
曹天骄1 天前
基于 Cloudflare Worker 构建分布式测速调度系统:KV 与 D1 数据层设计实战教程
分布式·缓存
Prince-Peng1 天前
技术架构系列 - 详解Redis
数据结构·数据库·redis·分布式·缓存·中间件·架构
七夜zippoe1 天前
Docker容器化Python应用最佳实践:从镜像优化到安全防护
python·docker·云原生·eureka·容器化
曹天骄1 天前
基于 Cloudflare Worker + KV 构建高性能分布式测速调度系统(工程实战)
分布式
奋进的芋圆1 天前
Spring Boot 3 高并发事务与分布式事务企业级完整解决方案
spring boot·分布式
灰子学技术1 天前
istio从0到1:产品落地过程的问题集锦
云原生·istio
淡泊if1 天前
Kafka部署模式详解:从单机到分布式集群的核心选择
分布式·kafka
鱼跃鹰飞1 天前
面试题:什么是时钟回拨问题?怎么解决
分布式·系统架构
无心水1 天前
分布式环境下定时任务与SELECT FOR UPDATE的陷阱与解决方案
分布式·后端·wpf·xxl-job·quartz·定时任务·selectforupdate