一举拿下高可用与分布式协调系统设计

简介: 一举拿下高可用与分布式协调系统设计

前言



上文中我们了解到, canal 可以通过订阅 binlog 日志来提供增量数据订阅和消费,通过这种方式可以实现数据库的实时备份,实时索引构建等


网络异常,图片无法展示
|


我们再来详细看看它的工作原理


网络异常,图片无法展示
|


如图示,每个 server 会启动多个实例(instance),每个实例会订阅不同表的 binlog,实例主要负责将 binlog 日志解析成程序易读的结构化数据(解析后包含变更记录的主键,字段变更后的值等),Canal Client 拿到结构化数据后再将其同步给其他 DB, MQ 等,同一时间某张表的 binlog 只能被一个 Instance 订阅处理,这是为了保障 binlog 处理的顺序性


现在问题来了,如果这台 Canal Server 挂掉了该怎么办,换句话说如何实现 server 的 HA(High Availability,即高可用)呢。


最容易想到的当然是准备几个备份的 Canal Server(以下简称备机),这样的话当主 Canal Server(工作中的 Server,以下简称主机)宕机了,备机就可以顶上去工作了。那么这里就涉及到两个问题


  1. 如果有三台机器(一台充当主机,两台充当备机),启动后到底哪个 Canal Server 为主机?
  2. 如果主机挂了,备机如何及时发现并接手主机的工作呢


显然这两个问题都涉及到多进程通信,所以最好引入一个中间层来处理,我们需要设计一个分布式协调系统来完成这两个需求,先给这个系统取个名字吧,姑且将其称为 Zookeeper,简称 ZK。


网络异常,图片无法展示
|


接下来我们来看看 ZK 需要如何设计才能满足我们上述的两个需求


设计分布式锁来解决主备角色问题



先来看问题一


如果有三台或更多机器启动的话,到底哪个 Canal Server 为主机?


显然分布式锁是可以满足这个需求的,三台机器启动后主动去获取这个分布式锁,获取成功的则充当主机,获取失败的则作为备机。


所以 ZK 必须要具有分布式锁的功能,可能有人说我可以引入 Redis,MySQL,因为这两者都具有分布式锁的功能,但这样相当于在一个系统里又引入了额外的组件,系统的复杂度上升了,而且也要保证新引入组件的高可用,不太可取,所以这次我们打算另辟蹊径直接在 ZK 中设计这样的分布式锁,首先要为 ZK 设计一个数据结构。


我们用类似 Linux 文件系统的树状结构来作为此分布式系统的数据结构。


网络异常,图片无法展示
|


根节点为 /,每个节点下的子节点名称是唯一的(也就意味着根节点下的所有节点名称唯一),这样的话三个 Canal Server 启动后,会首先尝试着在根节点下去创建同样名字的节点(假设为 /lock1),那么由于这个节点名称是唯一的,只能创建一个 /lock1 这样的节点,其他的再申请创建会失败,于是给我们一个思路:谁先创建成功,谁就为主节点,其余的为备机,这样的话主备问题就解决了。


如何让备机发现主机宕机了



再来看第二个问题


备机如何知道主机宕机?


前文提到主机会在 ZK 中创建一个节点 /lock1,创建成功即为主机,在创建节点之前,主机首先要通过 TCP 与分布式协调系统建立连接,这个连接会长期保活,主机会定期发送心跳来让 ZK 感知到它的存在,这样 ZK 就会知道主机还存活着,如果在指定的时间内(比如 2s )ZK 没有收到主机发来的心跳,就会认为主机宕机了,此时就会发通知给备机了。


这个通知该怎么发呢,ZK 不仅是为 Canal 服务的,还有很多其他的服务可能也需要在 ZK 上创建节点,不同的服务对应创建的节点都是不一样的,比如 canal 服务创建的节点是 /lock1,库存服务创建的节点为 /lock2,总不可能 canal 服务的主机宕机了,却要通知库存服务的备机吧。我们应该只通知当时创建 /lock1 失败的那些机器。


于是我们需要建立一个/lock1 与对应机器的映射列表,如下


网络异常,图片无法展示
|


每个创建 /lock1 节点失败的备机都被放到 key 为 /lock1 对应的列表里

我们把这个流程称为注册,这样的话每个节点对应的机器就可以查到了,如果创建 /lock1 节点的主机宕机了,ZK 发现后只要通过映射列表通知/lock1对应的备用机器列表就行啦(同时 ZK 要把 /lock1 这个节点给删掉,代表分布式锁释放了。)我们把这种备用机器能感知到节点被删除的机制称为 watch 机制,它的运行机制如下


网络异常,图片无法展示
|


1、 注册: client 创建节点 /lock1 失败后会监听此结点,一旦此节点消失会收到 ZK 的通知 2、 存储:将此 watcher 保存在客户端的 watcherManager 中,我们可以简单地

认为 watcher 的格式如下


网络异常,图片无法展示
|


注:为了简化讲解流程,我们给 watcher 加了一个 path 字段,实际 watcher 并没有,不过原理其实是一样的


path 即我们监听的结点,如本例中的 /lock1,keepState 即事件状态,比如连接成功,连接断开等,EventType 即节点对应的事件,如创建,删除,子节点变更等事件。

3、通知:ZK 将节点及其删除/创建事件通知到 client,client 通过这些信息在 watchManager 中找到相应的 watcher,就可以做相应的处理,比如监听到节点被删了,就可以感知到主机宕机了,它就可以尝试着去创建 /lock1 了,哪个创建成功则哪个备机就成为主机了。


通过以上的 watch 工作机制不难设计出我们的高可用方案,如下:


网络异常,图片无法展示
|


  1. 假设现在有 A,B 两台 Canal Server,为了成为主节点,它们首先向 zk 申请创建 /lock1节点
  2. A 创建成功后即成为主节点开始工作, B 再去创建节点则失败,会作为备机,但同时 B 会使用 watch 机制来监听 /lock1 节点(即将 B 机器注册到 ZK 中 /lock1 对应的机器列表中,/local1被删除后会通过此机器列表,B 就能感知到 A 宕机了)
  3. 一旦 A 不可用,则 ZK 会删除 /lock1 节点,同时也会通知 B,B 收到通知后,通过本地的 watchManager 找到此 watcher,得知是 /lock1 这个节点被删除事件后会再次尝试创建/lock1 节点,创建成功后则启动作为主节点工作,此刻 Canal Client 也会注意到 /lock1 创建了(Canal Client 启动后也用 watch 机制监听 /lock1 节点的创建事件),lock1 节点可以存储 Canal Server 地址的,这样的话通知 Canal Client 时可以把 B 地址传过来,Canal Client 就会与此机器建立连接了。


惊群效应与解决方案



按照上述的设计方案其实已经能满足 Canal 的高可用设计了,不过我们目前设计的  ZK 系统实现的分布式锁有两个问题


  1. 假设有几十台备机,当主机宕机后,这几十台备机都会尝试着去创建 /lock1 这个节点,但只有一台机器创建成功并成为主机,另外几十台机器在创建节点失败后则会马上处于等待状态,这就是我们所说的惊群效应(也叫羊群效应),不难发现惊群效应会造成资源的极大浪费,那么宕机后能否只通知一个备用节点响应,这样其他备用节点就不用群惊群乍了,能极大地节省资源。
  2. 当前的分布式锁是非公平锁,这样会造成饥饿现象,可能一些备机永远没有机会获取这个锁了,如何让它成为公平锁,让每个备用节点都有机会获取这个锁以让它们都有机会成为主机呢。


解决方案如下:


每个机器都会在 /lock1 下创建一个子节点,子节点的编号会按申请顺序递增,编号最小的那个节点表示其对应的机器持有了分布式锁,其余机器只会监听比它小一级的那个节点,这样当某个节点宕机了,只会通知这一个监听机器,避免了惊群效应


网络异常,图片无法展示
|


如图示,工作机制如下:


  1. 一开始,多台机器都在 /lock1 节点下创建以 sub-xxx 依序递增的节点,假设现在有一台机器创建了一个 sub-000001,则由于它的序号最小,表示它占有的分布式锁,其他机器则会依次创建 sub-000002sub-000003这样依序递增的节点。
  2. 每个节点对应的机器只会监听(watch)比它小一号的节点
  3. 这样的话,如果分布式锁释放了,则只会通知比它大一号的节点,如 sub-000001 节点对应的主机宕机了,只会通知机器 B(此时 ZK 也会把 sub-000001 这个临时节点给删了)这样 B 持有的节点序号最小,它也就持有了分布式锁。


通过这种方式我们可以也注意到机器获取锁的顺序与其创建的节点顺序保持了一致,也就实现了公平锁。


ZK 简介



以上就是利用 ZK 来实现分布式锁的一个简单案例,当然分布式锁只是它众多功能中的一个而已,作为一个分布式协调服务,它还有配置管理名字服务分布式同步以及集群管理等功能。先来简单看一下 ZK 的一些概念。


节点


我们已经知道 ZK 采用的是一种类似 Linux 文件系统的树形结构,树上的每个节点我们把它称为 Znode,Znode 节点分为以下四大类


  1. 临时节点:前文我们提到主机宕机后 /lock1 就会被删除,这种主机与 ZK 断开链接就被删除的节点我们称其为临时节点
  2. 临时顺序节点: 前文我们为了实现公平锁而创建的按申请次序顺序递增的节点
  3. 永久节点:客户端与 ZK 的连接断开后,节点还在,甚至 ZK 重启后节点也还在
  4. 永久顺序节点:在永久节点的基础上加上了按申请顺序递增的功能的节点


每个 Znode 节点都是可以存储数据的,默认是 1M,这样的话可以作为配置管理中心存储一些比较重要的数据


watcher 监听事件


前文我们提到了备份机器可以通过  watcher 机制来监听节点是否存在,从而可以及时响应这些事件作出处理,除了监听节点是否存在外,ZK 还提供了以下事件


public enum EventType {
     None (-1), // 客户端连接状态发生变化的时候 会受到none事件
     NodeCreated (1), // 节点创建事件
     NodeDeleted (2), // 节点删除事件
     NodeDataChanged (3), // 节点数据变化
     NodeChildrenChanged (4); // 子节点被创建,删除触发该事件
}
复制代码


ZK 就是通过这些 watcher 事件来实现分布式协调服务的,接下来我们来看看 ZK 在生产上的两个应用


ZK应用


作为 dubbo 注册中心


假设现在有两个服务,用户服务和订单服务,为了高可用,订单服务会部署多台机器,每个订单服务的机器都会在 ZK 中注册,每个节点为临时节点,如下


网络异常,图片无法展示
|


调用方(用户服务)会获取 /orders 下的所有子节点,即所有机器列表,也会监听 /orders 节点的 NodeChildrenChanged(子节点被创建或被删除)事件,然后通过一定的负载均衡算法选择一个来连接,假如其中一台机器如 192.168.11.1 宕机了,则其对应的临时节点会被删除,同时用户服务会能收到此 watch 事件,于是会重新获取 /orders 的子节点,此时由于宕机对应的子节点被删除了,所以只会获取 192.168.11.2 和  192.168.11.3 这两个子节点,这样就避免了连接 192.168.11.1 这个不可用的机器了


网络异常,图片无法展示
|


作为配置中心


在生产环境上 ,我们经常需要配置一些变动频繁,需要实时生效的数据,比如某个功能上线,我们需要针对某些用户做灰度,这个灰度规模是逐渐扩大的,我们就需要配置一下这个百分比来让每个机器实时生效,这时就可以让 ZK 作为配置中心,让每台线上的机器监听配置节点的 NodeDataChanged(节点数据变化) 事件,这样只要这个节点数据变化了,其他机器可以立即收到通知更新,让此修改立即生效,我司用的 360 开源的分布式配置管理系统 QConf 就是基于 ZK 开发的。


网络异常,图片无法展示
|


总结



通过本文相信大家不难理解 ZK 的工作机制,主要要理解它的树状结构,节点及 watch 工作机制,掌握了这些就能理解 ZK 作为配置中心,分布式锁,域名服务的原理,当然如果要更深入地了解 ZK,光掌握这些还不够,比如 ZK 如果只有一台,那会有单点故障,就要配置 ZK 集群,既然是集群,那如何保证数据一致呢,你需要去了解 ZAB 协议,选举机制等,建议大家看看《ZooKeeper分布式过程协同技术详解》这本书,会让你对 ZK 有更深入的理解。

相关文章
|
3月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
108 2
基于Redis的高可用分布式锁——RedLock
|
13天前
|
数据采集 存储 监控
公司监控软件:基于 PHP 的分布式监控系统设计
本文介绍了基于 PHP 的分布式监控系统的设计与实现。该系统包括监控节点、数据采集模块、数据传输模块和监控中心,能够高效地收集、传输和分析各节点的数据,确保系统的稳定运行和安全防护。通过示例代码展示了数据采集、传输及存储的具体实现方法,并强调了安全与可靠性的重要性。
36 3
|
4月前
|
负载均衡 Java 双11
使用Java构建高可用的分布式系统
使用Java构建高可用的分布式系统
|
1月前
|
分布式计算 Hadoop
Hadoop-27 ZooKeeper集群 集群配置启动 3台云服务器 myid集群 zoo.cfg多节点配置 分布式协调框架 Leader Follower Observer
Hadoop-27 ZooKeeper集群 集群配置启动 3台云服务器 myid集群 zoo.cfg多节点配置 分布式协调框架 Leader Follower Observer
47 1
|
1月前
|
存储 SQL 消息中间件
Hadoop-26 ZooKeeper集群 3台云服务器 基础概念简介与环境的配置使用 架构组成 分布式协调框架 Leader Follower Observer
Hadoop-26 ZooKeeper集群 3台云服务器 基础概念简介与环境的配置使用 架构组成 分布式协调框架 Leader Follower Observer
47 0
|
2月前
|
存储
cephFS高可用分布式文件系统部署指南
关于如何部署高可用的cephFS分布式文件系统,包括集群的搭建、验证高可用性以及实现两主一从架构的详细指南。
76 9
|
4月前
|
存储 运维 监控
如何设计高可用的分布式系统
【7月更文挑战第29天】设计高可用的分布式系统是一个复杂而细致的过程,需要从架构设计、冗余策略、故障转移与恢复、监控与告警等多个方面综合考虑。通过采用微服务架构、无状态服务、负载均衡、数据冗余、服务冗余、跨地域部署等策略,可以显著提高系统的可用性和可靠性。同时,建立完善的监控和告警体系,确保对系统的任何变化都能及时感知和处理。最终,通过不断的优化和改进,实现系统的高可用性目标。
|
4月前
|
消息中间件 Java 开发者
Spring Cloud微服务框架:构建高可用、分布式系统的现代架构
Spring Cloud是一个开源的微服务框架,旨在帮助开发者快速构建在分布式系统环境中运行的服务。它提供了一系列工具,用于在分布式系统中配置、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态等领域的支持。
183 5
|
3月前
|
存储 算法 NoSQL
(三)漫谈分布式之集群篇:探寻N个9高可用与PB级数据存储的实现原理!
本文来详细聊聊集群的各方面知识,为诸位量身打造出结构化的集群知识体系。
120 0
|
4月前
|
消息中间件 缓存 监控
如何设计一个秒杀系统,(高并发高可用分布式集群)
【7月更文挑战第4天】设计一个高并发、高可用的分布式秒杀系统是一个非常具有挑战性的任务,需要从架构、数据库、缓存、并发控制、降级限流等多个维度进行考虑。
128 1

热门文章

最新文章