作者:邓志文、王帆
01 为什么要容灾?
在小米内部,咱们运用 RocketMQ 来为各种在线事务供给音讯行列服务,比方商城订单、短信告知甚至用来搜集 IoT 设备的上报数据,可以说 RocketMQ 的可用性便是这些在线服务的生命线。作为软件开发者,咱们通常期望服务可以依照理想状态去运转:在没有Bug的前提下,系统可以供给正常的服务能力。
但现实的运维经历告知咱们这是不可能的,硬件毛病是非常常见的问题,比方内存毛病、磁盘毛病等,甚至是机房相关的毛病(专线毛病、机房拉闸等)。因而咱们需求对数据进行备份,运用多副本的方法来保证服务的高可用。Apache RocketMQ 规划上就支撑多副本、多节点容灾,比方 Master-Slave 架构、DLedger 布置形式。
在小米内部,因为是面向在线事务,服务的康复速度至关重要,而根据 Raft 协议的 DLedger 形式可以完结秒级 RTO,因而咱们在 2020 年初选用了 DLedger 架构作为基本的布置形式(在 5.0 中,主从形式也可以做到主动 failover)。支撑机房灾备需求添加额外的本钱,下面我将用三个灾备布置的实践事例,解说小米如何在本钱和可用性的取舍上去支撑灾备。
02 怎样去做容灾?
单机房高可用
实际在运用中,有许多事务是不需求机房级别容灾的,只需可以做到单机房高可用即可。Apache RocketMQ 自身便是分布式的音讯行列服务,可以很好的做到同机房多节点高可用,下面首要分享下小米在权衡本钱、可用性的前提下,如何去做布置架构的晋级优化。
咱们知道在 Raft 协议中,一般装备三个节点,利用机器冗余 + 主动选主切换来完结高可用的目标。因而在小米引入 RocketMQ 之初,单 Broker 组均布置三个 Broker 节点。一起为了保证集群中始终存在 Master 节点,咱们一般会至少布置两个 Broker 组,一个简略的布置架构图如下:

可以说是一个很基本的布置架构,在单个机房中,经过多副本、多Broker组做到了单机房容灾。但不难发现,这样做有一个很严峻的问题:资源浪费。RocketMQ 的从节点只需在客户端读取较旧的数据时才会起到从读的效果,其他时分都只是单纯地作为副本运转,机器利用率只需33%,这是让人无法忍受的。
出于本钱上的考虑,咱们需求从头思考现有的布置架构,如何才干利用起来从节点呢?一个很简略的思路便是节点混布:在从节点也布置 Broker 进程,让其可以作为 Master 来供给服务。比较巧合的是,社区其时也提出了 Broker Container 的概念,计划的原理是在 RocketMQ Broker 之上笼统一个 Container 角色,Container 用来办理 Broker 的增删改查,以此来到达单台服务主机上运转多个 Broker 的目的,详细架构图如下所示:

可以看到,Container 作为进程运转,原本的 Broker 被笼统为 Container 的一部分,相同的 3 台机器上咱们可以运转 9个 Broker 节点,组成三个 Broker组,每台服务主机上存在一个 Master 节点,运用 Container 对等布置 Broker 之后,每台服务主机都得到了利用,相同的机器数,理论上可以供给三倍的功用。
Container 是一种很好的布置思想:主从节点对等布置然后充分利用一切的机器。 咱们测验直接运用该计划,但遇到了一些问题:
-
Container 本质上是一个进程。不论其内运转了多少个 Broker ,咱们只需对其进行重启操作,都会影响该 Container 内部 Broker 相关的一切 Broker 组,晋级时会发生较为严峻的影响;
-
Container 自己维护 Broker 的上下线,无法与小米内部布置工具结合运用。
因而 Container 并不合适小米内部,但受 Broker Container的启示,咱们提出了另一种与之类似的布置计划——单机多实例。所谓单机多实例,即单台主机上布置多个 Broker 实例,服务主机便是咱们的 Container,Broker 以进程的方法运转,这样各个 Broker 之前不会相互影响,一起也可以和内部布置工具完美结合。一个简略的布置架构如下所示:

至此,小米内部完结了 RocketMQ 布置架构的第一次晋级,集群中的节点数直接削减了 2/3。在本钱优化的前提上依然供给 99.95% 的可用性保证。
多机房容灾 -Ⅰ
跟着事务的不断接入,一些事务提出了机房灾备的需求。机房毛病的概率尽管极低,可是一旦出现,其带来的影响是非常大的。比方机房毛病导致 RocketMQ 不可用,那么作为流量进口,将会影响到一切的依赖事务。
在多机房容灾上,咱们结合内部其他服务的布置经历,先提出了多集群多活的方法,即每个可用区布置一个集群,供给多个集群供事务容灾,计划布置架构如下:

用户视角看到的是三个独立的集群,需求在相同的可用区布置客户端去读写同机房的 RocketMQ 集群。举个比方:可用区1的客户端正常情况下访问可用区1的 RocketMQ 集群Cluster-1,当Cluster-1毛病时,用户需求手动更改客户端的衔接地址来切换集群,然后将流量转移到其他机房的集群中。用户可以经过装备下发去热更新衔接地址,也可以修改装备重启客户端来切换,但这一切的操作前提都是:需求事务感知到 RocketMQ 集群毛病,手动触发才干够。
▷长处
-
不用跨区同步数据,低延时(P99写入10ms)高吞吐(单Broker组写入TPS达100K)
-
布置架构简略,稳定性高
▷缺点
-
集群需预留灾备buffer,保证毛病时,存活集群可承载毛病集群的悉数流量
-
需求事务自己手动切换集群,不行灵活
-
若消费存在堆积,毛病集群的音讯将可能不会被消费,康复后可消费
▷出产耗时

多机房容灾 -Ⅱ
可以看到,事务如果选择以上方法接入的话,需求做必定的适配作业,该计划适用于流量较大的事务接入。可是有一些事务期望可以低本钱接入:不做适配,直接运用SDK接入,咱们结合 DLedger 主动切换的特性,实验性的布置了机房毛病服务主动 failover 的 形式,布置架构如下所示:

用户视角看到的便是一个独立的 RocketMQ 集群,运用 SDK 正常接入即可,无需任何适配。机房毛病时依赖 DLedger 主动切主做流量切换。
▷长处
-
布置便利,充分利用 RocketMQ 的原生能力
-
主动选主,事务接入便利,无需事务手动切换流量
▷缺点
-
跨机房布置,容易受网络动摇,集群颤动概率较大
-
跨机房布置,会添加写入延时,然后降低集群吞吐能力
▷出产耗时

多机房容灾 – PLUS
目前看来 RocketMQ 服务现已在小米完结了很好的落地,日音讯量也到达了千亿规划,但咱们仔细观察以上两个计划不难发现,尽管可以完结机房毛病切换,但都有必定的缺点,简要概略如下:
-
多机房容灾 -Ⅰ:同机房恳求,延时较低,但需事务手动切换集群
-
多机房容灾 -Ⅱ:主动切流、可消费历史数据,但对专线负载高,需三个Region才可布置
计划总是存在不行完美的地方,但不论作为服务的开发者仍是事务运用者,其实都期望可以在完结以下几个目标的前提下做到灾备:
1)低本钱:双Region可以完结布置;
2)低耗时:尽量同机房恳求,削减网络耗时;
3)主动切流:机房毛病时,可主动将流量切到正常的机房内。
为了完结以上的需求,咱们从 RocketMQ 自身的架构动身,期望可以以最低的改造本钱支撑灾备。咱们发现客户端都是依据 Namesrv 返回的元数据进行出产、消费,只需客户端可以在机房毛病时,可以依据元数据主动将流量切走即可,因而咱们将视角移到了客户端,期望从客户端上支撑灾备的功用。
RocketMQ 一切 Broker 都会将自己注册到 Namesrv 上去,一旦某个 Broker 组毛病,那么它的信息将会被从 Namesrv 中移除,客户端也就无法再向这类 Broker 组发送、拉取音讯。根据以上逻辑,只需咱们将 Broker组布置在不同机房中,便可以做到机房级别的灾备效果。布置架构如下:

咱们以一个实际的比方来解说以上计划的可行性:Topic-A 在两个可用区上均存在分区,SDK在运用时需求装备自己所在的region。
关于出产者来说,客户端只会向坐落相同可用区的分区发送音讯。例如:坐落可用区1的客户端只会向可用区1发送音讯,当可用区1毛病时,因为在可用区1不存在可写的分区,便会开端向可用区2发送音讯,然后完结出产侧的主动切流。消费者相同需求装备 region ,一切的消费实例会先依照可用区别别去做 rebalance:分区会优先被相同可用区的消费者去分配消费。当可用区1毛病时,因为出产者现已将流量切走,因而消费者不需求做特别改变就做到了消费主动切流。

该计划关于事务来说是一个可选项,事务可自行决定是否需求开启灾备形式,因而较为灵活,可以说是结合了以往两种机房灾备计划的长处,可是仍有不足之处,比方毛病集群在毛病期间历史音讯不可被消费等,后续也会不断的优化计划。
03 来做个总结吧!
本文介绍了四种布置形式,针对不同的事务需求供给不同的布置形式,总结如下:

目前以上计划在小米内部均有详细的事务场景,音讯量约占总体的 90%,未来也会逐步将剩下流量相关集群悉数晋级为机房灾备集群,然后供给 99.99% 的可用性服务能力。
