一问一答:为什么项目中要使用消息队列?

:为什么你们的项目里要使用消息队列?

:我们在分布式项目里引入消息队列,核心是解决系统耦合、接口响应慢、高峰期被流量打垮这三大核心痛点,总结下来有三个不可替代的核心价值:系统解耦、异步提速、流量削峰,同时还能带来重试容错、广播通知这些额外的收益,我分别给你讲清楚每个价值的实际场景。


1. 核心价值一:系统解耦,彻底解决上下游强绑定的问题

如果不用消息队列,会出现非常严重的系统耦合问题:比如我们的订单系统(A 系统)生成订单后,需要给库存、支付、物流、积分这几个下游系统同步数据。不用 MQ 的话,订单系统要挨个调用下游的接口,加一个新的下游系统就要改订单系统的代码,某个下游不用了也要删代码;同时还要考虑下游系统挂了怎么办、超时了要不要重试,订单系统要操心所有下游的状态,维护成本极高。

用了消息队列之后,订单系统只需要做一件事:订单生成后,把消息发到 MQ 里就完事了。哪个下游系统需要这个数据,自己去 MQ 里订阅消费就行;加新系统直接订阅 MQ,不用改订单系统的代码;下游系统挂了也完全不影响订单系统的正常运行。这样一来,订单系统和所有下游系统彻底解耦,不用再关心下游的状态,代码维护成本直接降了一个量级。

2. 核心价值二:异步提速,大幅提升接口响应速度

不用消息队列的话,同步调用会让接口响应变得极慢:比如用户下单接口,订单系统本地写数据库只需要 3ms,但要同步调用库存扣减 300ms、支付校验 450ms、物流初始化 200ms,整个接口总耗时接近 1 秒。而互联网产品的用户操作,要求接口 200ms 以内返回才会无感知,1 秒的响应时间用户会觉得非常卡顿。

用了消息队列之后,订单系统本地处理完,把 3 条消息发到 MQ 里只需要 5ms,整个接口总耗时只有 8ms,直接给用户返回结果,用户几乎是点一下就完成了下单。下游的库存、支付、物流系统,自己慢慢从 MQ 里拉消息处理就行,完全不影响用户的操作体验,接口响应速度直接提升了上百倍。

3. 核心价值三:流量削峰,保证系统高峰期不崩溃

不用消息队列的话,突发的高峰期流量会直接打垮系统:比如我们的电商系统,平时每秒只有 50 个下单请求,数据库完全扛得住;但到了中午秒杀、或者大促的时候,每秒会突增到 5000 个请求,直接打到数据库的话,MySQL 每秒最多扛 2000 个请求,会被直接打挂,整个系统直接崩溃。

用了消息队列之后,用户的下单请求会先写入 MQ,订单系统按照数据库能扛住的上限,每秒固定从 MQ 里拉 2000 个请求处理。高峰期的流量会暂时积压在 MQ 里,等高峰期过了,系统再快速把积压的消息消费完。这样既不会把数据库打垮,也不会丢失用户的下单请求,哪怕是大促高峰期,系统也能稳定运行。


补充:消息队列的额外收益 & 使用注意事项

除了这三个核心价值,消息队列还能给系统带来很多额外的能力:

  1. 重试容错:下游系统处理消息失败了,MQ 可以自动重试,不用上游系统自己写复杂的重试逻辑,大幅提升系统的容错性;
  2. 广播通知:一条消息可以被多个下游系统同时消费,比如用户注册成功,发一条消息,积分、优惠券、短信通知系统都能同时收到,不用上游系统挨个调用接口;
  3. 流量控制:可以通过调整消费速度,控制系统的处理压力,避免被突发流量打垮。

同时我也清楚,引入消息队列也会带来系统复杂度提升的问题,比如要处理消息丢失、重复消费、消息顺序性、分布式事务这些问题,所以我们不会盲目引入 MQ,只会在真正需要解耦、异步、削峰的场景里使用,同时会做好消息的可靠性保障。

一问一答:Kafka、RabbitMQ、RocketMQ 的优缺点与选型

:Kafka、RabbitMQ、RocketMQ 这三款主流消息队列,分别有什么优缺点?你们项目里是怎么选型的?:这三款是目前国内互联网公司最主流的消息队列,没有绝对的优劣之分,核心是要根据业务场景、团队技术栈来匹配选型 —— 选对了能解决核心痛点,选错了反而会给自己挖坑。我分别给你讲清楚每款的核心特点、优劣势和适用场景,再说说我们项目的实际选型逻辑。


一、三款消息队列的核心优劣势 & 适用场景

1. RabbitMQ

核心优点

  • 延迟是三款里最低的,能做到微秒级响应,对实时性要求高的场景特别友好;
  • 基于 Erlang 语言开发,天生适配高并发场景,性能稳定,参数优化后基本不会丢消息,可靠性拉满;
  • 功能特别全,几乎所有消息模型都支持,死信队列、延迟队列、消息重试这些高级功能都是开箱即用,不用自己二次开发;
  • 社区极其活跃,几乎所有编程语言都有对应客户端,和 Spring 生态无缝集成,学习成本极低,新手也能快速上手。

核心缺点

  • 单机吞吐量是三款里最低的,只有万级,比另外两款低了一个数量级,扛不住电商大促那种超高并发的大流量场景;
  • 基于 Erlang 开发,国内 Java 技术团队很少有人能吃透它的源码,出了底层问题很难排查和定制化修改,技术可控性差;
  • 不支持大量 topic,一旦 topic 数量到几百上千,吞吐量会直接断崖式下跌。

适用场景:中小型公司、对实时性要求高、没有极端超高并发需求的业务,比如金融支付、订单处理,还有需要和多语言第三方系统集成的场景。

2. RocketMQ

核心优点

  • 阿里开源并捐给 Apache,纯 Java 语言开发,国内 Java 团队能轻松看懂源码,不管是定制化开发还是问题排查,都能完全掌控,技术可控性极强;
  • 单机吞吐量能到 10 万级,和 Kafka 持平,完全能扛住电商大促每秒几万订单的超高并发场景;
  • 对 topic 的兼容性特别好,哪怕 topic 数量到几百几千个,吞吐量也只会有小幅下降,特别适合业务复杂、topic 数量多的中大型系统;
  • 功能完全贴合国内互联网业务,原生支持事务消息、延迟消息、消息轨迹、消息过滤这些电商、金融场景必备的高级功能。尤其是事务消息,能完美解决分布式系统的数据一致性问题,不用自己再写复杂的补偿逻辑;
  • 分布式架构设计,可用性极高,支持主从副本、异地多活容灾,能满足金融级的高可用要求。

核心缺点

  • 国际社区活跃度不如 Kafka,海外使用范围较小;
  • 部分高级功能只有阿里云商业版才有,开源版有一定的功能阉割;
  • 和大数据生态的集成度远不如 Kafka,不适合大数据流处理场景。

适用场景:大型互联网公司、Java 技术栈的电商 / 金融业务,比如秒杀、订单系统、广告投放、用户推送,尤其是需要分布式事务、复杂消息功能的高并发业务场景。

3. Kafka

核心优点

  • 单机吞吐量是三款里最高的,能稳定做到 10 万级以上,哪怕超高并发场景,性能也极其稳定;
  • 是大数据领域的事实标准,和 Flink、Spark、Hadoop 等大数据组件无缝集成,业内几乎所有实时计算场景都用它,生态特别完善;
  • 分布式多副本架构,可用性极高,少数节点宕机完全不影响集群运行,还能支撑海量数据的长期存储和回溯;
  • 经过参数优化后,可以做到消息 0 丢失,可靠性完全有保障;
  • 全球社区极其活跃,文档和最佳实践非常丰富,基本不会出现找不到解决方案的问题。

核心缺点

  • 不支持大量 topic,topic 数量到几百个后,吞吐量会大幅下降;
  • 延迟是毫秒级,比 RabbitMQ 的微秒级差很多,不适合对极致低延迟有要求的业务;
  • 功能非常精简,核心只有消息收发能力,没有事务消息、延迟消息这类业务常用的高级功能,有需求得自己二次开发;
  • 设计上偏向流处理场景,不适合传统的业务消息通信场景。

适用场景:大数据领域的日志采集、实时数据计算、监控数据上报,还有需要超高吞吐量的事件驱动架构、流处理场景。


二、核心特性对比表

表格

核心特性

RabbitMQ

RocketMQ

Kafka

单机吞吐量

万级,比另外两款低一个数量级

10 万级,高吞吐

10 万级以上,业内顶尖的高吞吐

topic 数量对吞吐量的影响

topic 数量过百后,吞吐量大幅下降

topic 到几百几千,吞吐量只有小幅下降

topic 过百后,吞吐量大幅下降

消息时延

微秒级,三款里最低

毫秒级

毫秒级以内

可用性

高,主从架构实现高可用

非常高,分布式架构,支持异地容灾

非常高,分布式多副本架构,节点宕机不影响集群

消息可靠性

基本不丢

参数优化后可做到 0 丢失

参数优化后可做到 0 丢失

开发语言

Erlang

Java

Scala/Java


三、通用选型建议 & 我们项目的实际选型

通用选型逻辑

我给不同团队的选型建议很简单,完全匹配场景和团队能力即可:

  1. 中小型公司,技术实力一般,业务无极端高并发需求,想要开箱即用、功能全的 MQ,优先选RabbitMQ,上手快、稳定可靠,运维成本极低;
  2. 大型互联网公司,Java 技术栈,做电商、金融类业务,需要高吞吐、高可用,还有事务消息、延迟消息这类业务高级功能,优先选RocketMQ
  3. 大数据场景,做日志采集、实时计算、超高吞吐的流处理,直接选Kafka,它是这个领域的业内标准,基本不会踩坑。
我们项目的实际选型

我们的核心电商订单系统,最终选的是RocketMQ,选型的核心原因有这几点:第一,我们整个技术栈都是 Java,团队能完全吃透 RocketMQ 的源码,不管是二次开发还是线上问题排查,都能完全掌控,不会出现出了问题抓瞎的情况;第二,我们的订单场景有强数据一致性需求,RocketMQ 的原生事务消息,能完美保证订单创建、库存扣减、积分发放这几个环节的分布式数据一致性,不用自己写复杂的补偿逻辑;第三,我们大促高峰期有每秒几万的订单流量,RocketMQ 的高吞吐能力完全能支撑,它的流量削峰能力也特别适配大促场景;第四,我们有订单超时自动取消的需求,RocketMQ 的原生延迟消息能直接实现,不用自己做定时任务扫表,大幅降低了开发成本;最后,我们的业务拆分很细,有几十个业务 topic,RocketMQ 对多 topic 的兼容性很好,不会出现吞吐量下降的问题,完全能满足我们的业务需求。

一问一答:如何保证消息队列的高可用?

:如何保证消息队列的高可用?:消息队列的高可用,核心是解决「单节点故障导致服务不可用、数据丢失」的问题,不同 MQ 的高可用架构设计完全不同,我分别给你讲清楚主流 RabbitMQ、Kafka 的高可用实现原理,同时也会补充我们线上的实际配置方案。


一、RabbitMQ 的高可用实现

RabbitMQ 是基于主从架构实现高可用的,一共有三种部署模式,只有镜像集群模式能实现真正的高可用:

  1. 单机模式纯 Demo 级别的模式,只适合本地开发测试用,生产环境完全不会用,没有任何高可用能力,单机宕机服务就直接不可用。
  2. 普通集群模式(无高可用)这个模式只是为了提升 MQ 的吞吐量,完全没有高可用保障。它的原理是:在多台机器上启动多个 RabbitMQ 节点,你创建的 queue 只会存在其中一个节点上,其他节点只会同步 queue 的元数据(配置信息)。消费的时候如果连接到了非 queue 所在的节点,这个节点会从 queue 所在的节点拉取数据再转发给消费者。它的核心问题:如果 queue 所在的节点宕机了,其他节点就没法拉取数据,如果你没开启消息持久化,消息就直接丢了;就算开启了持久化,也要等这个节点恢复才能继续消费,完全做不到高可用。
  3. 镜像集群模式(真正的高可用)这是 RabbitMQ 生产环境用的高可用模式。它的原理是:你创建的 queue,无论是元数据还是里面的消息,都会完整同步到集群里的所有 RabbitMQ 节点,每个节点都有这个 queue 的完整镜像副本。每次写消息到 queue 的时候,会自动把消息同步到所有节点的 queue 上。高可用保障:任何一个机器宕机都完全不影响,其他节点还有 queue 的完整数据,消费者可以直接连到其他节点继续消费,不会出现服务不可用、数据丢失的问题。开启方式也很简单,在 RabbitMQ 的管理后台新增镜像集群策略,指定消息需要同步的节点数量,创建 queue 的时候应用这个策略即可。它的缺点也很明显:一是消息同步到所有节点,会带来很大的网络带宽和性能开销;二是不是分布式架构,没有扩展性可言,如果某个 queue 的负载很高,新增机器也会同步这个 queue 的全量数据,没法线性提升 queue 的处理性能。

二、Kafka 的高可用实现

Kafka 是天然的分布式消息队列,它的高可用是基于「partition 分区 + replica 副本机制 + leader 选举」实现的,在保证高可用的同时,还能线性提升系统吞吐量。

  1. 基础架构认知Kafka 集群由多个 broker 节点组成,你创建的 topic 会被拆分成多个 partition 分区,每个 partition 可以分散在不同的 broker 上,每个 partition 只存 topic 的一部分数据,是天然的分布式存储。Kafka 0.8 版本之前是没有高可用机制的,如果某个 broker 宕机了,这个 broker 上的 partition 就直接废了,数据会丢,也没法读写,完全没有高可用保障。
  2. 副本(replica)机制:高可用的核心Kafka 0.8 之后引入了副本机制,这是它高可用的核心:每个 partition 都会有多个副本,这些副本会均匀分散在不同的 broker 机器上,避免单台机器故障丢失所有副本。所有副本会选举出一个 leader,生产者和消费者的所有读写请求,都只会和 leader 交互,其他副本作为 follower,会主动从 leader 拉取数据同步。
  3. 高可用的保障逻辑
    • 故障自动容灾:如果某个 broker 宕机了,这个 broker 上的 partition 的副本,在其他机器上还有完整备份。如果宕机的 broker 上有 partition 的 leader,Kafka 会自动从剩下的 follower 里重新选举出一个新的 leader,生产者和消费者直接和新 leader 交互即可,服务完全不会中断,数据也不会丢失。
    • 数据一致性保障:生产者写消息到 leader 后,leader 会先把消息落盘到本地磁盘,follower 从 leader 拉取到消息、完成同步后,会给 leader 发送 ack 确认。leader 收到所有 follower 的 ack 后,才会给生产者返回写成功。消费者也只能读到已经被所有 follower 同步完成的消息,保证数据不会丢失、不会出现不一致。
  1. 优势它的高可用设计比 RabbitMQ 更优秀,在保证高可用的同时,还能通过增加 broker 节点、拆分更多 partition,线性提升整个集群的吞吐量,非常适合大数据、高并发的流处理场景。

三、我们线上的高可用实际配置

  1. 金融支付场景用的 RabbitMQ,我们用的是 3 节点镜像集群,所有核心业务 queue 都配置了同步到所有节点的镜像策略,同时开启了消息持久化,哪怕两个节点宕机,剩下的节点也能完整提供服务,完全不影响支付链路。
  2. 日志采集、实时数据计算场景用的 Kafka,我们配置了 2 副本,acks=all(所有副本同步完成才返回写成功),同时设置了最小同步副本数为 1,既保证了高可用,也平衡了写入性能,线上运行两年多,没有出现过因为 broker 宕机导致的服务不可用、数据丢失问题。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐