同程旅行基于 RocketMQ 高可用架构实践
简介: 我们在几年前决定引入 MQ 时,市场上已经有不少成熟的解决方案,比如 RabbitMQ , ActiveMQ,NSQ,Kafka 等。考虑到稳定性、维护成本、公司技术栈等因素,我们选择了 RocketMQ。

背景介绍
为何选择 RocketMQ
我们在几年前决定引入 MQ 时,市场上已经有不少成熟的解决方案,比如 RabbitMQ , ActiveMQ,NSQ,Kafka 等。考虑到稳定性、维护成本、公司技术栈等因素,我们选择了 RocketMQ :
- 纯 Java 开发,无依赖,使用简单,出现问题能 hold ;
- 经过阿里双十一考验,性能、稳定性可以保障;
- 功能实用,发送端:同步、异步、单边、延时发送;消费端:消息重置,重试队列,死信队列;
- 社区活跃,出问题能及时沟通解决。
使用情况
- 主要用于削峰、解耦、异步处理;
- 已在火车票、机票、酒店等核心业务广泛使用,扛住巨大的微信入口流量;
- 在支付、订单、出票、数据同步等核心流程广泛使用;
- 每天 1000+ 亿条消息周转。
下图是 MQ 接入框架图
由于公司技术栈原因,client sdk 我们提供了 java sdk ;对于其他语言,收敛到 http proxy ,屏蔽语言细节,节约维护成本。按照各大业务线,对后端存储节点进行了隔离,相互不影响。

MQ 双中心改造
之前单机房出现过网络故障,对业务影响较大。为保障业务高可用,同城双中心改造提上了日程。
为何做双中心
- 单机房故障业务可用;
- 保证数据可靠:若所有数据都在一个机房,一旦机房故障,数据有丢失风险;
- 横向扩容:单机房容量有限,多机房可分担流量。
双中心方案
做双中心之前,对同城双中心方案作了些调研,主要有冷(热)备份、双活两种。(当时社区 Dledger 版本还没出现,Dledger 版本完全可做为双中心的一种可选方案。)
1)同城冷(热)备份
两个独立的 MQ 集群, 用户流量写到一个主集群,数据实时同步到备用集群,社区有成熟的 RocketMQ Replicator 方案,需要定期同步元数据,比如主题,消费组,消费进度等。

2)同城双活
两个独立 MQ 集群,用户流量写到各自机房的 MQ 集群,数据相互不同步。
平时业务写入各自机房的 MQ 集群,若一个机房挂了,可以将用户请求流量全部切到另一个机房,消息也会生产到另一个机房。

对于双活方案,需要解决 MQ 集群域名。
1)若两个集群用一个域名,域名可以动态解析到各自机房。此方式要求生产、消费必须在同一个机房。假如生产在 idc1 ,消费在 idc2 ,这样生产、消费各自连接一个集群,没法消费数据。
2)若一个集群一个域名,业务方改动较大,我们之前对外服务的集群是单中心部署的,业务方已经大量接入,此方案推广较困难。
为尽可能减少业务方改动,域名只能继续使用之前的域名,最终我们采用一个 Global MQ 集群,跨双机房,无论业务是单中心部署还是双中心部署都不影响;而且只要升级客户端即可,无需改动任何代码。
双中心诉求
- 就近原则:生产者在 A 机房,生产的消息存于 A 机房 broker ; 消费者在 A 机房,消费的消息来自 A 机房 broker 。
- 单机房故障:生产正常,消息不丢。
- broker 主节点故障:自动选主。
就近原则
简单说,就是确定两件事:
- 节点(客户端节点,服务端节点)如何判断自己在哪个 idc;
- 客户端节点如何判断服务端节点在哪个 idc。
如何判断自己在哪个 idc?
1) ip 查询
节点启动时可以获取自身 ip ,通过公司内部的组件查询所在的机房。
2)环境感知
需要与运维同学一起配合,在节点装机时,将自身的一些元数据,比如机房信息等写入本地配置文件,启动时直接读写配置文件即可。
我们采用了第二个方案,无组件依赖,配置文件中 logicIdcUK 的值为机房标志。
客户端节点如何识别在同一个机房的服务端节点?

客户端节点可以拿到服务端节点的 ip 以及 broker 名称的,因此:
- ip 查询:通过公司内部组件查询 ip 所在机房信息;
- broker 名称增加机房信息:在配置文件中,将机房信息添加到 broker 名称上;
- 协议层增加机房标识:服务端节点向元数据系统注册时,将自身的机房信息一起注册。
相对于前两者,实现起来略复杂,改动了协议层, 我们采用了第二种与第三种结合的方式。
就近生产
基于上述分析,就近生产思路很清晰,默认优先本机房就近生产;
若本机房的服务节点不可用,可以尝试扩机房生产,业务可以根据实际需要具体配置。

就近消费
优先本机房消费,默认情况下又要保证所有消息能被消费。
队列分配算法采用按机房分配队列
- 每个机房消息平均分给此机房消费端;
- 此机房没消费端,平分给其他机房消费端。
伪代码如下:
Map<String, Set> mqs = classifyMQByIdc(mqAll);
Map<String, Set> cids = classifyCidByIdc(cidAll);
Set<> result = new HashSet<>;
for(element in mqs){
result.add(allocateMQAveragely(element, cids, cid)); //cid为当前客户端
}

消费场景主要是消费端单边部署与双边部署。
单边部署时,消费端默认会拉取每个机房的所有消息。

双边部署时,消费端只会消费自己所在机房的消息,要注意每个机房的实际生产量与消费端的数量,防止出现某一个机房消费端过少。

单机房故障
- 每组 broker 配置
一主两从,一主一从在一机房,一从在另一机房;某一从同步完消息,消息即发送成功。
- 单机房故障
消息生产跨机房;未消费消息在另一机房继续被消费。
故障切主
在某一组 broker 主节点出现故障时,为保障整个集群的可用性,需要在 slave 中选主并切换。要做到这一点,首先得有个broker 主故障的仲裁系统,即 nameserver(以下简称 ns )元数据系统(类似于 redis 中的哨兵)。
ns 元数据系统中的节点位于三个机房(有一个第三方的云机房,在云上部署 ns 节点,元数据量不大,延时可以接受),三个机房的 ns 节点通过 raft 协议选一个leader,broker 节点会将元数据同步给 leader, leader 在将元数据同步给 follower 。
客户端节点获取元数据时, 从 leader,follower 中均可读取数据。

切主流程
- 若 nameserver leader 监控到 broker 主节点异常, 并要求其他 follower 确认;半数 follower 认为 broker 节点异常,则 leader 通知在 broker 从节点中选主,同步进度大的从节点选为主;
- 新选举的 broker 主节点执行切换动作并注册到元数据系统;
- 生产端无法向旧 broker 主节点发送消息。
流程图如下

切中心演练
用户请求负载到双中心,下面的操作先将流量切到二中心---回归双中心---切到一中心。确保每个中心均可承担全量用户请求。
先将用户流量全部切到二中心

流量回归双中心,并切到一中心

回顾
- 全局 Global 集群
- 就近原则
- 一主二从,写过半消息即及写入成功
- 元数据系统 raft 选主
- broker 主节点故障,自动选主
MQ 平台治理
即使系统高性能、高可用,倘若随便使用或使用不规范,也会带来各种各样的问题,增加了不必要的维护成本,因此必要的治理手段不可或缺。
目的
让系统更稳定
- 及时告警
- 快速定位、止损
治理哪些方面
主题/消费组治理
- 申请使用
生产环境 MQ 集群,我们关闭了自动创建主题与消费组,使用前需要先申请并记录主题与消费组的项目标识与使用人。一旦出现问题,我们能够立即找到主题与消费组的负责人,了解相关情况。若存在测试,灰度,生产等多套环境,可以一次申请多个集群同时生效的方式,避免逐个集群申请的麻烦。
- 生产速度
为避免业务疏忽发送大量无用的消息,有必要在服务端对主题生产速度进行流控,避免这个主题挤占其他主题的处理资源。
- 消息积压
对消息堆积敏感的消费组,使用方可设置消息堆积数量的阈值以及报警方式,超过这个阈值,立即通知使用方;亦可设置消息堆积时间的阈值,超过一段时间没被消费,立即通知使用方。
- 消费节点掉线
消费节点下线或一段时间无响应,需要通知给使用方。
客户端治理
- 发送、消费耗时检测
监控发送/消费一条消息的耗时,检测出性能过低的应用,通知使用方着手改造以提升性能;同时监控消息体大小,对消息体大小平均超过 10 KB 的项目,推动项目启用压缩或消息重构,将消息体控制在 10 KB 以内。
- 消息链路追踪
一条消息由哪个 ip 、在哪个时间点发送,又由哪些 ip 、在哪个时间点消费,再加上服务端统计的消息接收、消息推送的信息,构成了一条简单的消息链路追踪,将消息的生命周期串联起来,使用方可通过查询msgId或事先设置的 key 查看消息、排查问题。
- 过低或有隐患版本检测
随着功能的不断迭代,sdk 版本也会升级并可能引入风险。定时上报 sdk 版本,推动使用方升级有问题或过低的版本。
服务端治理
- 集群健康巡检
如何判断一个集群是健康的?定时检测集群中节点数量、集群写入 tps 、消费 tps ,并模拟用户生产、消费消息。
- 集群性能巡检
性能指标最终反映在处理消息生产与消费的时间上。服务端统计处理每个生产、消费请求的时间,一个统计周期内,若存在一定比例的消息处理时间过长,则认为这个节点性能有问题;引起性能问题的原因主要是系统物理瓶颈,比如磁盘 io util 使用率过高,cpu load 高等,这些硬件指标通过夜鹰监控系统自动报警。
- 集群高可用
高可用主要针对 broker 中 master 节点由于软硬件故障无法正常工作,slave 节点自动被切换为 master ,适合消息顺序、集群完整性有要求的场景。
部分后台操作展示
主题与消费组申请

生产,消费,堆积实时统计

集群监控

踩过的坑
社区对 MQ 系统经历了长时间的改进与沉淀,我们在使用过程中也到过一些问题,要求我们能从深入了解源码,做到出现问题心不慌,快速止损。
- 新老消费端并存时,我们实现的队列分配算法不兼容,做到兼容即可;
- 主题、消费组数量多,注册耗时过长,内存 oom ,通过压缩缩短注册时间,社区已修复;
- topic 长度判断不一致,导致重启丢消息,社区已修复;
- centos 6.6 版本中,broker 进程假死,升级 os 版本即可。
MQ 未来展望
目前消息保留时间较短,不方便对问题排查以及数据预测,我们接下来将对历史消息进行归档以及基于此的数据预测。
- 历史数据归档
- 底层存储剥离,计算与存储分离
- 基于历史数据,完成更多数据预测
- 服务端升级到 Dledger ,确保消息的严格一致
原文链接
本文为阿里云原创内容,未经允许不得转载。
同程旅行基于 RocketMQ 高可用架构实践的更多相关文章
- vivo 基于原生 RabbitMQ 的高可用架构实践
一.背景说明 vivo 在 2016 年引入 RabbitMQ,基于开源 RabbitMQ 进行扩展,向业务提供消息中间件服务. 2016~2018年,所有业务均使用一个集群,随着业务规模的增长,集群 ...
- Keepalived+LVS DR模式高可用架构实践
Keepalived最初是为LVS设计,专门监控各服务器节点的状态(LVS不带健康检查功能,所以使用keepalived进行健康检查),后来加入了VRRP(虚拟路由热备协议(Virtual Route ...
- 美团点评基于MGR的CMDB高可用架构搭建之路【转】
王志朋 美团点评DBA 曾在京东金融担任DBA,目前就职于美团点评,主要负责金融业务线数据库及基础组件数据库的运维. MySQL Group Replication(以下简称MGR),于5.7.17版 ...
- 实现基于Keepalived高可用集群网站架构的多种方法
实现基于Keepalived高可用集群网站架构 随着业务的发展,网站的访问量越来越大,网站访问量已经从原来的1000QPS,变为3000QPS,目前业务已经通过集群LVS架构可做到随时拓展,后端节点已 ...
- 实现基于Haproxy+Keepalived负载均衡高可用架构
1.项目介绍: 上上期我们实现了keepalived主从高可用集群网站架构,随着公司业务的发展,公司负载均衡服务已经实现四层负载均衡,但业务的复杂程度提升,公司要求把mobile手机站点作为单独的服务 ...
- 基于Consul的数据库高可用架构【转】
几个月没有更新博客了,已经长草了,特意来除草.本次主要分享如何利用consul来实现redis以及mysql的高可用.以前的公司mysql是单机单实例,高可用MHA加vip就能搞定,新公司mysql是 ...
- 032:基于Consul和MGR的MySQL高可用架构
目录 一.Consul 1.Consul简介 2.准备环境 3.Consul 安装 4.Consul配置文件 5.Consul 服务检查脚本 6.Consul启动 二.MGR搭建 1.MGR配置 2. ...
- mysql +keeplive+drbd高可用架构(MHA基于监听端口VIP的高可用)
1MySQL+DRBD+keepalived高可用架构 DRBD(DistributedReplicatedBlockDevice)是一个基于块设备级别在远程服务器直接同步和镜像数据的开源软件,类似于 ...
- 近千节点的Redis Cluster高可用集群案例:优酷蓝鲸优化实战(摘自高可用架构)
(原创)2016-07-26 吴建超 高可用架构导读:Redis Cluster 作者建议的最大集群规模 1,000 节点,目前优酷在蓝鲸项目中管理了超过 700 台节点,积累了 Redis Clus ...
- 【转】单表60亿记录等大数据场景的MySQL优化和运维之道 | 高可用架构
此文是根据杨尚刚在[QCON高可用架构群]中,针对MySQL在单表海量记录等场景下,业界广泛关注的MySQL问题的经验分享整理而成,转发请注明出处. 杨尚刚,美图公司数据库高级DBA,负责美图后端数据 ...
随机推荐
- 初探修模的三维模型OBJ格式轻量化压缩的遇到常见问题与处理方法
初探修模的三维模型OBJ格式轻量化压缩的遇到常见问题与处理方法 在对经过修模的三维模型进行OBJ格式轻量化压缩处理的过程中,可能会遇到一些常见问题.以下是一些常见问题以及相应的处理方法: 1.顶点丢失 ...
- 《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南 - 第7章
本章勘误: 暂无,等待细心的你告诉我哦. 本章注解: 暂无 本章释疑: 暂无,等待你的提问 致谢: MVP 林德熙 MVP 吕毅 sPhinX 相关链接 试读记录
- 【Docker】Dockerfile基础知识,相信你一定有所收获
Dockerfile常用命令 FROM: 继承基础镜像 MAINTAINER:镜像制作作者信息 RUN: 用来执行shell命令 EXPOSE: 暴露端口号 CMD: 启动容器默认执行的命令,会被覆盖 ...
- Unit 1 Computer hardware【石家庄铁道大学-专业英语课程复习资料】
Unit 1 Computer hardware 1.Introduction of computer A computer is a machine that can be instructed t ...
- Scala 函数闭包和柯里化
1 package com.atguigu.function 2 3 object HighFunction { 4 def main(args: Array[String]): Unit = { 5 ...
- 22 axios和axios拦截器
1. axios 由于jquery有严重的地狱回调逻辑. 再加上jquery的性能逐年跟不上市场节奏. 很多前端工程师采用axios来发送ajax. 相比jquery. axios更加灵活. 且容易使 ...
- Node 项目通过 .npmrc 文件指定依赖安装源
背景 npm 命令运行时,往往通过命令行指定相关配置,最常用的便是使用 --registry 来指定依赖的安装源. npm install --registry=https://registry.np ...
- C++ 智能指针和内存管理:使用指南和技巧
C++是一门强大的编程语言,但是在内存管理方面却存在着一些问题.手动管理内存不仅费时费力,而且容易出错.因此,C++中引入了智能指针这一概念,以更好地管理内存. 什么是智能指针? 在C++中,内存的分 ...
- C# PropertyChanged 事件-数据绑定
在.NET平台上,数据绑定是一项令人十分愉快的技术.利用数据绑定能减少代码,简化控制逻辑. 通常,可以将某个对象的一个属性绑定到一个可视化的控件上,当属性值改变时,控件上的显示数据也随之发生变化.要实 ...
- C#读 .properties文件
引用:https://www.cnblogs.com/wardensky/p/5331851.html properties文件 MONGO_URL = mongodb://172.16.20.3/s ...