【解构云原生】RocketMQ高可用方案调研及On K8S设计展望(上)

一、前言

RocketMQ是阿里捐赠给Apache并于2018年作为顶级项目孵化的分布式消息引擎,目前已经加入CNCF成为云原生核心消息中间件。国内厂商如美团,滴滴、爱奇艺、顺丰和民生银行等都在使用,网易集团内部云音乐和严选也在使用RocketMQ,并对周边功能进行了扩展定制。

鉴于RocketMQ的广泛使用,网易杭州研究院云中间件团队将基于轻舟微服务平台,通过和网易云音乐、严选共建使RocketMQ上云,简化RocketMQ部署和运维工作,实现集团内RocketMQ标准化。

本文主要分两部分:

o 调研RocketMQ的集群高可用方案

o 云原生时代RocketMQ on K8s集群设计概要

二、基本概念

2.1 RocketMQ基本概念

RocketMQ架构如下图所示(以经典Master/Slave模式为例):


核心概念:

o Name Server:无状态集群(数据可能暂时不一致),提供命名服务,更新和发现Topic-Broker服务。

o Broker:负责存储、转发消息,分为Master和Slave。Broker启动后需要将自己的Topic和Group等路由信息注册至所有Name Server;随后每隔30s定期向Name Server上报路由信息。

o 生产者:与 Name Server 任一节点建立长链接,定期从 Name Server 读取路由信息,并与提供 Topic 服务的对应 Master Broker 建立长链接。

o 消费者:与 Name Server 任一节点建立长链接,定期从 Name Server 拉取路由信息,并与提供 Topic 服务的 Master Broker、Slave Broker 建立长连接。Consumer 既可以从 Master Broker 订阅消息,也可以从 Slave Broker 订阅消息,订阅规则由 Broker 配置决定。支持Pull和Push两种模型。

o Topic:表示一类消息的集合,每个主题包含若干条消息,每条消息只能属于一个主题,是RocketMQ进行消息订阅的基本单位。

支持的features:

o 普消息收发

o 普通顺序消息(Normal Ordered Message):普通顺序消费模式下,消费者通过同一个消费队列收到的消息是有顺序的,不同消息队列收到的消息则可能是无顺序的。

o 严格顺序消息(Strictly Ordered Message):严格顺序消息模式下,消费者收到的所有消息均是有顺序的。

o 集群消费(Clustering):集群消费模式下,相同Consumer Group的每个Consumer实例平均分摊消息。

o 广播消费(Broadcasting):广播消费模式下,相同Consumer Group的每个Consumer实例都接收全量的消息。

o 事务消息: RocketMQ事务消息(Transactional Message)是指应用本地事务和发送消息操作可以被定义到全局事务中,要么同时成功,要么同时失败。RocketMQ的事务消息提供类似 X/Open XA 的分布事务功能,通过事务消息能达到分布式事务的最终一致。

o 延迟消息: 定时消息(延迟队列)是指消息发送到broker后,不会立即被消费,等待特定时间投递给真正的topic。 broker有配置项messageDelayLevel,默认值为“1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”,18个level。

o 死信队列:死信队列用于处理无法被正常消费的消息。当一条消息初次消费失败,消息队列会自动进行消息重试;达到最大重试次数后,若消费依然失败,则表明消费者在正常情况下无法正确地消费该消息,此时,消息队列 不会立刻将消息丢弃,而是将其发送到该消费者对应的特殊队列中。RocketMQ将这种正常情况下无法被消费的消息称为死信消息(Dead-Letter Message),将存储死信消息的特殊队列称为死信队列(Dead-Letter Queue)。

参考:RocketMQ开源features

三、RocketMQ高可用方案现状

分布式系统遵循CAP原则,即:一致性、可用性和分区容错性三者无法在分布式系统中被同时满足,并且最多只能满足其中两个,需要根据业务进行衡量取舍。不同的解决方案对各项指标的支持程度各有侧重。基于CAP原则,很难设计出一种高可用方案能同时够满足所有指标的最优值。

Name Server无状态,可线性扩展,天然高可用。生产者和消费者客户端伴随着业务集群部署,因此下面重点讨论Broker的高可用机制,以下是对RocketMQ高可用方案的调研汇总。

3.1 Master/Slave方案

部署架构图可参考本文第二章概念介绍图,核心是broker部署的时候指定角色,通过主从复制实现,支持同步和异步两种模式。


o Broker目前仅支持主备复制,Master宕机不可修复需要手动修改备的配置文件(此时Slave是Read-Only)然后重启,不具备宕机自动failover能力

o Master/Slave复制模式分为同步和异步,用户可以根据业务场景进行trade-off,在性能和数据可靠性之间选择一个

o RocketMQ的Topic可以分片在不同的broker上面,一个broker挂了,生产者可以继续投递消息到其他broker,已经挂的broker上的消息必须等人工重启才能进一步处理

参考:RocketMQ的HA机制解析(上)——Master/Slave模式

3.2 Dledger方案

官方开源4.5版本已经利用Dledger支持自动failover,当Broker的主节点挂后,从节点通过raft选举能自动切换成主节点。


核心是实现了基于raft协议的DLedgerCommitLog,保证Broker内commitLog文件的一致性。

RocketMQ-on-DLedger-Group

o RocketMQ-on-DLedger-Group 是指一组相同名称的 Broker,至少需要 3 个节点,通过 Raft 自动选举出一个 Leader,其余节点 作为

o Follower,并在 Leader 和 Follower 之间复制数据以保证高可用

o RocketMQ-on-DLedger-Group能自动容灾切换,并保证数据一致

o RocketMQ-on-DLedger-Group是可以水平扩展的,也即可以部署任意多个RocketMQ-on-DLedger-Group同时对外提供服务

原理分析

o 在Broker的配置文件中增加enableDLegerCommitLog=true及raft选举相关的配置重启broker即可

o Broker启动初始化BrokerController时会创建DLedgerCommitLog和DLedgerRoleChangeHandler

o DLedgerCommitLog初始化时创建DLedgerServer在RocketMQ-on-DLedger-Group内完成raft选举逻辑,DLedgerCommitLog的putMessage方法可以保证消息写入在RocketMQ-on-DLedger-Group内多数节点完成

o DLedgerRoleChangeHandler监听raft选举结果,调用BrokerController的changeToSlave和changeToMaster方法完成角色切换逻辑

数据可靠性

DLedger机制只保证commitlog文件的一致性,主从切换过程中是否会有数据丢失和消费进度错乱呢?

o 消息消费进度的同步是slave定时向master拉取进行更新,存在时延,master挂掉后,消费者继续消费,此时消费的broker不一定就能选举成为master,因此消费进度可能丢失,存在重复消费的可能性

o raft协议保证了写入成功的消息存在多数节点上,最终选举出来的主节点的当前复制进度一定是比绝大多数的从节点要大,并且也会等于承偌给客户端的已提交偏移量,故不会丢消息

参考:源码分析 RocketMQ DLedger 多副本即主从切换实现原理

3.3 阿里分享HAController方案

通过阿里中间件团队博客调研到一个RocketMQ跨机房高可用方案。需要借助ZK和HAController组件实现。


核心流程如下:

o RocketMQ以临时节点的方式向ZK注册当前状态

o HAController监听Zookeeper上RocketMQ当前状态的变更

o HAController根据集群的当前状态,控制主备状态机的切换并向Zookeeper汇报最新主备状态机

o RocketMQ作为观察者监听Zookeeper上主备状态机的变更。当发现主备状态机变化时,根据最新的状态机更改当前状态

采用主备同步复制的方式避免故障时消息的丢失。数据同步过程中,通过维护一个递增的全局唯一SequenceID来保证数据强一致。同时引入故障自动恢复机制以降低故障恢复时间,提升系统的可用性。

下图描述了RocketMQ高可用架构中有限状态机的转换:


1) 第一个节点启动后,Controller控制状态机切换为单主状态,通知启动节点以Master角色提供服务。

2) 第二个节点启动后,Controller控制状态机切换成异步复制状态。Master通过异步方式向Slave复制数据。

3) 当Slave的数据即将赶上Master,Controller控制状态机切换成半同步状态,此时命中Master的写请求会被Hold住,直到Master以异步方式向Slave复制了所有差异的数据。

4) 当半同步状态下Slave的数据完全赶上Master时,Controller控制状态机切换成同步复制模式,Mater开始以同步方式向Slave复制数据。该状态下任一节点出现故障,其它节点能够在秒级内切换到单主状态继续提供服务。

Controller组件控制RocketMQ按照单主状态,异步复制状态,半同步状态,同步复制状态的顺序进行状态机切换。中间状态的停留时间与主备之间的数据差异以及网络带宽有关,但最终都会稳定在同步复制状态下。

参考:万亿级数据洪峰下的分布式消息引擎

3.4 网易云音乐Nydus方案

整体架构如下图:


o 增加Nginx组件,提供发现NameServer能力,由运维将nameserver列表填写到hotdoc中。避免NameServer变更业务重新配置上线

o 增加FailOver组件,自动完成Broker主从切换

o 降级组件提供消息发送失败的处理,在消息发送失败的情况下client会将消息发送到容灾集群,由降级组件统一处理保证发送方业务的稳定性

o 自定义巡检、告警和大盘等组件

根据业务的诉求,总结了需要提供的QPS能力,整体如下:


针对Level0,Level1的QOS,我们提供异步复制异步双写模式优先保证业务可用性,在机器宕机时能快速恢复。针对Level2的QOS要求,我们提供了同步复制同步双写,优先保证消息不丢失。

云音乐基于开源的Rplication,修正Replication复制的问题,增加CommitlogOffset上报,Failover自动探测选主的策略实现了根据QOS可配置的Broker高可用。

消息队列高可用概要设计:


据图分析和理解,该方案和阿里HAController有限状态机变化类似,区别在于增加了服务QOS,根据设定的QOS达到指定的终态。

1) 第一个节点启动后,Controller控制状态机切换为单主状态,通知启动节点以AsyncMaster角色提供服务。

2) Controller发现启动新的Slave后,将全局状态改为异步复制状态,主节点AsyncMaster,从节点Slave模式运行,判断QOS如果是Level0和Level1则进入终态

3) 如果QOS是Lever2, Controller根据Slave同步Master的进度,如主备之间offset相差50条消息,全局状态进入同步复制状态,此时主节点按照SyncMaster模式运行,从节点依然是Slave。

4)QOS是level0或level1,AsyncMaster挂掉的情况下,Controller根据之前主备上报的CommitlogOffset提升Slave为AsyncMaster,此时可能会丢失部分消息(取决于异步复制进度)

5)QOS是level2,SyncMaster宕机,因为是同步复制,所以可以直接提升Slave为AsyncMaster单节点运行,等待新的节点加入

6)Slave宕机,告警即可,无需切换;单节点运行状态下如果宕机也是直接告警。

参考:网易标准中间件-RocketMQ-云音乐Nydus

3.5 总结

总体来说高可用方案两个模式,一种是Master/Slave+Failover组件维护Broker状态机实现HA,另一种是基于raft协议保证commitLog一致性。

方案对比如下:


网易公司内部大规模RocketMQ集群使用案例(网易云音乐Nydus)来看,Master/Slave+Failover组件方案稳定性和性能有较高的保障,线上环境在用,风险较低。

下篇将解析RocketMQ on K8s的意义和设计思路,敬请期待。

本文来源:网易云     作者:李海燕,网易杭州研究院云计算技术部工程师

下篇:《【解构云原生】RocketMQ高可用方案调研及On K8S设计展望(下)》