Java高频考点:消息队列详解
一问一答:为什么项目中要使用消息队列?
问:为什么你们的项目里要使用消息队列?
答:我们在分布式项目里引入消息队列,核心是解决系统耦合、接口响应慢、高峰期被流量打垮这三大核心痛点,总结下来有三个不可替代的核心价值:系统解耦、异步提速、流量削峰,同时还能带来重试容错、广播通知这些额外的收益,我分别给你讲清楚每个价值的实际场景。
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 里,等高峰期过了,系统再快速把积压的消息消费完。这样既不会把数据库打垮,也不会丢失用户的下单请求,哪怕是大促高峰期,系统也能稳定运行。
补充:消息队列的额外收益 & 使用注意事项
除了这三个核心价值,消息队列还能给系统带来很多额外的能力:
- 重试容错:下游系统处理消息失败了,MQ 可以自动重试,不用上游系统自己写复杂的重试逻辑,大幅提升系统的容错性;
- 广播通知:一条消息可以被多个下游系统同时消费,比如用户注册成功,发一条消息,积分、优惠券、短信通知系统都能同时收到,不用上游系统挨个调用接口;
- 流量控制:可以通过调整消费速度,控制系统的处理压力,避免被突发流量打垮。
同时我也清楚,引入消息队列也会带来系统复杂度提升的问题,比如要处理消息丢失、重复消费、消息顺序性、分布式事务这些问题,所以我们不会盲目引入 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 |
三、通用选型建议 & 我们项目的实际选型
通用选型逻辑
我给不同团队的选型建议很简单,完全匹配场景和团队能力即可:
- 中小型公司,技术实力一般,业务无极端高并发需求,想要开箱即用、功能全的 MQ,优先选RabbitMQ,上手快、稳定可靠,运维成本极低;
- 大型互联网公司,Java 技术栈,做电商、金融类业务,需要高吞吐、高可用,还有事务消息、延迟消息这类业务高级功能,优先选RocketMQ;
- 大数据场景,做日志采集、实时计算、超高吞吐的流处理,直接选Kafka,它是这个领域的业内标准,基本不会踩坑。
我们项目的实际选型
我们的核心电商订单系统,最终选的是RocketMQ,选型的核心原因有这几点:第一,我们整个技术栈都是 Java,团队能完全吃透 RocketMQ 的源码,不管是二次开发还是线上问题排查,都能完全掌控,不会出现出了问题抓瞎的情况;第二,我们的订单场景有强数据一致性需求,RocketMQ 的原生事务消息,能完美保证订单创建、库存扣减、积分发放这几个环节的分布式数据一致性,不用自己写复杂的补偿逻辑;第三,我们大促高峰期有每秒几万的订单流量,RocketMQ 的高吞吐能力完全能支撑,它的流量削峰能力也特别适配大促场景;第四,我们有订单超时自动取消的需求,RocketMQ 的原生延迟消息能直接实现,不用自己做定时任务扫表,大幅降低了开发成本;最后,我们的业务拆分很细,有几十个业务 topic,RocketMQ 对多 topic 的兼容性很好,不会出现吞吐量下降的问题,完全能满足我们的业务需求。
一问一答:如何保证消息队列的高可用?
问:如何保证消息队列的高可用?答:消息队列的高可用,核心是解决「单节点故障导致服务不可用、数据丢失」的问题,不同 MQ 的高可用架构设计完全不同,我分别给你讲清楚主流 RabbitMQ、Kafka 的高可用实现原理,同时也会补充我们线上的实际配置方案。
一、RabbitMQ 的高可用实现
RabbitMQ 是基于主从架构实现高可用的,一共有三种部署模式,只有镜像集群模式能实现真正的高可用:
- 单机模式纯 Demo 级别的模式,只适合本地开发测试用,生产环境完全不会用,没有任何高可用能力,单机宕机服务就直接不可用。
- 普通集群模式(无高可用)这个模式只是为了提升 MQ 的吞吐量,完全没有高可用保障。它的原理是:在多台机器上启动多个 RabbitMQ 节点,你创建的 queue 只会存在其中一个节点上,其他节点只会同步 queue 的元数据(配置信息)。消费的时候如果连接到了非 queue 所在的节点,这个节点会从 queue 所在的节点拉取数据再转发给消费者。它的核心问题:如果 queue 所在的节点宕机了,其他节点就没法拉取数据,如果你没开启消息持久化,消息就直接丢了;就算开启了持久化,也要等这个节点恢复才能继续消费,完全做不到高可用。
- 镜像集群模式(真正的高可用)这是 RabbitMQ 生产环境用的高可用模式。它的原理是:你创建的 queue,无论是元数据还是里面的消息,都会完整同步到集群里的所有 RabbitMQ 节点,每个节点都有这个 queue 的完整镜像副本。每次写消息到 queue 的时候,会自动把消息同步到所有节点的 queue 上。高可用保障:任何一个机器宕机都完全不影响,其他节点还有 queue 的完整数据,消费者可以直接连到其他节点继续消费,不会出现服务不可用、数据丢失的问题。开启方式也很简单,在 RabbitMQ 的管理后台新增镜像集群策略,指定消息需要同步的节点数量,创建 queue 的时候应用这个策略即可。它的缺点也很明显:一是消息同步到所有节点,会带来很大的网络带宽和性能开销;二是不是分布式架构,没有扩展性可言,如果某个 queue 的负载很高,新增机器也会同步这个 queue 的全量数据,没法线性提升 queue 的处理性能。
二、Kafka 的高可用实现
Kafka 是天然的分布式消息队列,它的高可用是基于「partition 分区 + replica 副本机制 + leader 选举」实现的,在保证高可用的同时,还能线性提升系统吞吐量。
- 基础架构认知Kafka 集群由多个 broker 节点组成,你创建的 topic 会被拆分成多个 partition 分区,每个 partition 可以分散在不同的 broker 上,每个 partition 只存 topic 的一部分数据,是天然的分布式存储。Kafka 0.8 版本之前是没有高可用机制的,如果某个 broker 宕机了,这个 broker 上的 partition 就直接废了,数据会丢,也没法读写,完全没有高可用保障。
- 副本(replica)机制:高可用的核心Kafka 0.8 之后引入了副本机制,这是它高可用的核心:每个 partition 都会有多个副本,这些副本会均匀分散在不同的 broker 机器上,避免单台机器故障丢失所有副本。所有副本会选举出一个 leader,生产者和消费者的所有读写请求,都只会和 leader 交互,其他副本作为 follower,会主动从 leader 拉取数据同步。
- 高可用的保障逻辑
-
- 故障自动容灾:如果某个 broker 宕机了,这个 broker 上的 partition 的副本,在其他机器上还有完整备份。如果宕机的 broker 上有 partition 的 leader,Kafka 会自动从剩下的 follower 里重新选举出一个新的 leader,生产者和消费者直接和新 leader 交互即可,服务完全不会中断,数据也不会丢失。
- 数据一致性保障:生产者写消息到 leader 后,leader 会先把消息落盘到本地磁盘,follower 从 leader 拉取到消息、完成同步后,会给 leader 发送 ack 确认。leader 收到所有 follower 的 ack 后,才会给生产者返回写成功。消费者也只能读到已经被所有 follower 同步完成的消息,保证数据不会丢失、不会出现不一致。
- 优势它的高可用设计比 RabbitMQ 更优秀,在保证高可用的同时,还能通过增加 broker 节点、拆分更多 partition,线性提升整个集群的吞吐量,非常适合大数据、高并发的流处理场景。
三、我们线上的高可用实际配置
- 金融支付场景用的 RabbitMQ,我们用的是 3 节点镜像集群,所有核心业务 queue 都配置了同步到所有节点的镜像策略,同时开启了消息持久化,哪怕两个节点宕机,剩下的节点也能完整提供服务,完全不影响支付链路。
- 日志采集、实时数据计算场景用的 Kafka,我们配置了 2 副本,acks=all(所有副本同步完成才返回写成功),同时设置了最小同步副本数为 1,既保证了高可用,也平衡了写入性能,线上运行两年多,没有出现过因为 broker 宕机导致的服务不可用、数据丢失问题。
更多推荐
所有评论(0)