阅读 142
高可用的升级-RocketMQ知识体系7

高可用的升级-RocketMQ知识体系7

一直以来,在多地多中心的消息发送场景下,如何保障数据的完整性和一致性是一个技术难点。在 RocketMQ 4.5 版本之前,RocketMQ 只有 Master/Slave 一种部署方式,一组 broker 中有一个 Master ,有零到多个

Slave,Slave 通过同步复制或异步复制的方式去同步 Master 数据。Master/Slave 部署模式,提供了一定的高可用性。
但这样的部署模式,有一定缺陷。比如故障转移方面,如果主节点挂了,还需要人为手动进行重启或者切换,无法自动将一个从节点转换为主节点。那么什么样的多副本架构可以来解决这个问题呢?首先我们来看看多副本技术的演进。

多副本技术的演进

Master/Slave

多副本最早的是 Master/Slave 架构,即简单地用 Slave 去同步 Master 的数据,RocketMQ 最早也是这种实现。分为同步模式(Sync Mode)和异步模式(Async Mode),区别就是 Master 是否等数据同步到 Slave 之后再返回 Client。这两种方式目前在 RocketMQ 社区广泛使用的版本中都有支持,也可以看我前面分享的文章。

基于 Zookeeper 服务

随着分布式领域开启了快速发展。在 Hadoop 生态中,诞生了一个基于 Paxos 算法选举 Leader 的分布式协调服务 ZooKeeper。

由于 ZooKeeper 本身拥有高可用和高可靠的特性,随之诞生了很多基于 ZooKeeper 的高可用高可靠的系统。

具体做法如下图所示:

图片

Based on Zookeeper/Etcd

如图所示,假如系统里有 3 个节点,通过 ZooKeeper 提供的一些接口,可以从 3 个节点中自动的选出一个 Master 来。选出一个 Master 后,另外两个没成功的就自然变成 Slave。选完之后,后续过程与传统实现方式中的复制一样。故基于 ZooKeeper 的系统与基于 Master/Slave 系统最大的区别就是:选 Master 的过程由手动选举变成依赖一个第三方的服务(比如 ZooKeeper 或 Etcd)的选举。

但是基于 ZooKeeper 的服务也带来一个比较严重的问题:依赖加重。因为运维 ZooKeeper 是一件很复杂的事情。

基于 Raft 服务方式

因为 ZooKeeper 的复杂性,又有了以下 Raft 的方式。Raft 可以认为是 Paxos 的简化版。基于 Raft 的方式与上述两种方式最大的区别是:leader 的选举是由自己完成的。比如一个系统有 3 个节点,这 3 个节点的 leader 是利用 Raft 的算法通过协调选举自己去完成的,选举完成之后,Master 到 Slave 同步的过程仍然与传统方式类似。最大的好处就是去除了依赖,即本身变得很简单,可以自己完成自己的协调

实现高可靠和高可用的方法优劣对比

Master/Slave,Based on ZooKeeper/Etcd 和 Raft,这三种是目前分布式系统中,做到高可靠和高可用的基本的实现方法,各有优劣。

Master/Slave

优点:实现简单

缺点:不能自动控制节点切换,一旦出了问题,需要人为介入。

基于 Zookeeper/Etcd

优点:可以自动切换节点

缺点:运维成本很高,因为 ZooKeeper 本身就很难运维。

Raft

优点:可以自己协调,并且去除依赖。

缺点:实现 Raft,在编码上比较困难。

多副本架构首先需要解决自动故障转移的问题,本质上来说是自动选主的问题。这个问题的解决方案基本可以分为两种:

  • 利用第三方协调服务集群完成选主,比如 zookeeper 或者 etcd。这种方案会引入了重量级外部组件,加重部署,运维和故障诊断成本,比如在维护 RocketMQ 集群还需要维护 zookeeper 集群,并且 zookeeper 集群故障会影响到 RocketMQ 集群。

  • 利用 raft 协议来完成一个自动选主,raft 协议相比前者的优点是不需要引入外部组件,自动选主逻辑集成到各个节点的进程中,节点之间通过通信就可以完成选主。

目前很多中间件都使用了raft 协议或使用了变种的raft协议,如mongodb .还有新版的kafka,放弃了zookeeper,

将元数据存储在 Kafka 本身,而不是存储 ZooKeeper 这样的外部系统中。新版kafka的Quorum 控制器使用新的 KRaft 协议来确保元数据在仲裁中被精确地复制。这个协议在很多方面与 ZooKeeper 的 ZAB 协议和 Raft 相似。

RocketMQ也选择用 raft 协议来解决这个问题。

关于Raft

我们知道在分布式领域,始终都要面临的一个挑战就是:数据一致性.

Paxos。如今它是业界公认此类问题的最有效解。虽然Paxos在理论界得到了高度认可,但是却给工程界带来了难题。因为这个算法本身比较晦涩,并且抽象,缺少很多实现细节。这让许多工程师大为头疼

Raft是为解决Paxos难以理解和实现的问题而提出的。

Raft 算法的工作流程主要包含五个部分:

  • 领导选举(Leader election):在集群初始化或者旧领导异常情况下,选举出一个新的领导。

  • 日志复制(Log replication): 当有新的日志写入时,领导能把它复制到集群中大多数节点上。

  • 集群成员变更(Cluster Membership changes): 当集群有扩容或者缩容的需求,集群各节点能准确感知哪些节点新加入或者被去除。

  • 日志压缩(Log compaction): 当写入的日志文件越来越大,重启时节点回放(replay)日志的时间将无限延长,并且新节点加入集群时传送日志文件也会无限拉大。需要定期对日志文件进行重整压缩。

  • 读写一致性(Read/write consistency): 客户端作为集群的外部组件,当一个客户端写入新数据时,能保证后续所有客户端都能读到最新的值。

数据一致性的几层语义:

  • 数据的写入顺序要保持一致。否则可能出现很多不预期的情况,比如:旧值覆盖新值。先删后增变成先增后删,数据消失了

  • 对成功写入的数据供认不讳。如果数据被集群表明写入成功,那么集群各节点都应该认可并接受这个结果,而不会出现某些节点不知情的情况。

  • 数据写入成功保证持久化的。如果集群表明数据写入成功,数据却没落盘。这时宕机了,那么数据就丢失了。

Raft在 保序性、共识性、持久性都能很好的支持这就能证明:

在假定领导永不宕机的前提下,Raft是能够保证集群数据一致性的。

Leader在非正常运行情况下,推选出的新Leader至少拥有所有已提交的日志,从而保证数据一致性。

因为Raft规定:一切写入操作必须由Leader管控,所以选主这段时间客户端的写入会被告知失败或者进行不断重试。这里其实一定程度上牺牲了集群的可用性来保证一致性。然而就像CAP定理告诉我们的,分布式系统不能既保证一致性C,又保证可用性A。而Raft集群选择了 C和 P,也就一定程度失去了A。

所以,Raft算法能保证集群的数据一致性。

什么是 DLedger

Dledger 的定位

DLedger 就是一个基于 raft 协议的 commitlog 存储库,

Dledger 作为一个轻量级的 Java Library,它的作用就是将 Raft 有关于算法方面的内容全部抽象掉,开发人员只需要关心业务即可也是 RocketMQ 实现新的高可用多副本架构的关键。

图片

如上图所示,Dledger 只做一件事情,就是 CommitLog。Etcd 虽然也实现了 Raft 协议,但它是自己封装的一个服务,对外提供的接口全是跟它自己的业务相关的。在这种对 Raft 的抽象中,可以简单理解为有一个 StateMachine 和 CommitLog。CommitLog 是具体的写入日志、操作记录,StateMachine 是根据这些操作记录构建出来的系统的状态。在这样抽象之后,Etcd 对外提供的是自己的 StateMachine 的一些服务。Dledger 的定位就是把上一层的 StateMachine 给去除,只留下 CommitLog。这样的话,系统就只需要实现一件事:就是把操作日志变得高可用和高可靠。

Dledger 的架构

图片

从前面介绍的多副本技术的演进可以知道,我们要做的主要有两件事:选举和复制,对应到上面的架构图中,也就是两个核心类:DLedgerLeaderElector 和 DLedgerStore,选举和文件存储。选出 leader 后,再由 leader 去接收数据的写入,同时同步到其他的 follower,这样就完成了整个 Raft 的写入过程。

DLedger 的优化

Raft 协议复制过程可以分为四步,先是发送消息给 leader,leader 除了本地存储之外,会把消息复制给 follower,然后等待follower 确认,如果得到多数节点确认,该消息就可以被提交,并向客户端返回发送成功的确认。

DLedger对于复制过程有以下优化:

  • 1、对于复制过程,DLedger  采用一个异步线程模型提高吞吐量和性能。

  • 2、DLedger 中,leader 向所有 follower 发送日志也是完全相互独立和并发的,leader 为每个 follower 分配一个线程去复制日志 这是一个独立并发的复制过程。

  • 3、在独立并发的复制过程内,DLedger设计并实现日志并行复制的方案,不再需要等待前一个日志复制完成再复制下一个日志,只需在 follower 中维护一个按照日志索引排序请求列表, follower 线程按照索引顺序串行处理这些复制请求。而对于并行复制后可能出现数据缺失问题,可以通过少量数据重传解决。

通过以上3点,优化这一复制过程。

在可靠性方面DLedger也对网络分区做了优化,而且也对DLedger 做了可靠性测试:

DLedger对网络分区的优化

图片

如果出现上图的网络分区,n2与集群中的其他节点发生了网络隔离,按照 raft 论文实现,n2会一直请求投票,但得不到多数的投票,term 一直增大。一旦网络恢复后,n2就会去打断正在正常复制的n1和n3,进行重新选举。为了解决这种情况,DLedger 的实现改进了 raft 协议,请求投票过程分成了多个阶段,其中有两个重要阶段:WAIT_TO_REVOTE和WAIT_TO_VOTE_NEXT。WAIT_TO_REVOTE是初始状态,这个状态请求投票时不会增加 term,WAIT_TO_VOTE_NEXT则会在下一轮请求投票开始前增加 term。对于图中n2情况,当有效的投票数量没有达到多数量时。可以将节点状态设置WAIT_TO_REVOTE,term 就不会增加。通过这个方法,提高了Dledger对网络分区的容忍性。

DLedger 可靠性测试

官方不仅测试对称网络分区故障,还测试了其他故障下 Dledger 表现情况,包括随机杀死节点,随机暂停一些节点的进程模拟慢节点的状况,以及 bridge、partition-majorities-ring 等复杂的非对称网络分区。在这些故障下,DLedger 都保证了一致性,验证了 DLedger 有很好可靠性。

Dledger 的应用

在 RocketMQ 上的应用

RocketMQ 4.5 版本发布后,可以采用 RocketMQ on DLedger 方式进行部署。DLedger commitlog 代替了原来的 commitlog,使得 commitlog 拥有了选举复制能力,然后通过角色透传的方式,raft 角色透传给外部 broker 角色,leader 对应原来的 master,follower 和 candidate 对应原来的 slave。

图片

因此 RocketMQ 的 broker 拥有了自动故障转移的能力。在一组 broker 中, Master 挂了以后,依靠 DLedger 自动选主能力,会重新选出 leader,然后通过角色透传变成新的 Master。

Dledger构建高可用的嵌入式 KV 存储

DLedger 还可以构建高可用的嵌入式 KV 存储。

我们没有一个嵌入式且高可用的解决方案。RocksDB 可以直接用,但是它本身不支持高可用。(Rocks DB 是Facebook开源的单机版数据库)

有了 DLedger 之后,我们把对一些数据的操作记录到 DLedger 中,然后根据数据量或者实际需求,恢复到hashmap 或者 rocksdb 中,从而构建一致的、高可用的 KV 存储系统,应用到元信息管理等场景。

图片

参考:

developer.aliyun.com/article/713…

blog.csdn.net/csdn\_lhs/a…

本文使用 文章同步助手 同步

文章分类
后端