消息队列:微服务的异步通信枢纽

在微服务架构中,服务间的通信方式主要分为两种:同步和异步。

同步调用就像打电话------你拨过去,对方必须在线上听着,你一句我一句,实时响应。HTTP、RPC 都是同步调用。它的特点是实时性好,但缺点也很明显:调用方必须等待被调用方返回结果,如果被调用方慢了或者挂了,调用方也得跟着等。

异步调用就像发短信------你把消息发出去,不用等对方回复,该干嘛干嘛,对方看到消息后再处理。消息队列就是实现这种异步通信的核心中间件。

这篇文章讲清楚消息队列在微服务中解决了什么问题、有哪些典型场景、以及引入它之后会带来哪些新挑战。


一、消息队列解决了什么问题

1.1 解耦

没有消息队列时,服务A要调用服务B、服务C、服务D,A的代码里得写死对这三个服务的调用。哪天不需要调用D了,或者要新增一个服务E,A的代码就得改。

引入消息队列后,A只需要把消息发给队列,谁需要谁自己去队列取。A不关心谁来消费,消费者也不关心谁发的消息。生产者和消费者完全解耦。

1.2 削峰填谷

电商大促时,下单流量可能是平时的几十倍。如果所有流量直接打到数据库,数据库很可能扛不住。

引入消息队列后,请求先写入队列,消费者按照自己能处理的速度慢慢消费。流量高峰被队列"削"平了,保护了下游系统。等到流量低谷时,消费者还能继续处理积压的消息,把高峰期的活慢慢干完。

1.3 异步处理

用户注册后,系统需要做一系列事情:写数据库、发送欢迎邮件、发放新人优惠券、通知运营系统。

如果同步做,用户得等着所有步骤完成才能收到响应。引入消息队列后,主流程只做最核心的写数据库操作,其他非核心操作通过消息队列异步处理,用户响应时间大大缩短。

1.4 最终一致性

跨服务的分布式事务很难用强一致性保证。消息队列配合本地事务,可以实现最终一致性。

以下单扣库存为例:订单服务先写订单(本地事务),同时发一条"扣库存"消息到队列。库存服务消费消息执行扣减。如果库存服务失败了,消息会重新投递,或者走补偿流程。最终两边数据会达成一致,只是中间有短暂的不一致窗口。


二、核心概念

消息队列有几个基本概念需要先理清:

生产者:发送消息的一方。它负责把消息发到队列,不需要关心谁会消费。

消费者:接收消息的一方。它从队列取消息并处理,不需要关心是谁发的。

消息:生产者和消费者之间传递的数据,通常是一个 JSON 字符串。

队列:消息的临时存放地。它像一个缓冲区,保证消息不会因为消费者暂时不可用而丢失。

主题:有些消息队列(如 Kafka、RocketMQ)用主题做消息分类。一个主题可以对应多个队列。

Broker:消息队列的服务端,负责接收、存储、投递消息。


三、常见的消息队列产品

产品 特点 适用场景
RocketMQ 阿里开源,吞吐量高,支持事务消息 电商、订单、金融交易
RabbitMQ 老牌稳定,功能丰富,易用性好 中小规模、复杂路由
Kafka 高吞吐,持久化强,适合日志类 日志收集、大数据流处理
Pulsar 云原生,存储计算分离 新兴项目、多租户场景

对于大部分 SpringBoot 微服务项目,RabbitMQ 和 RocketMQ 是最常见的选择。


四、典型使用场景

4.1 订单超时自动取消

用户下单后如果一直没有支付,系统需要自动取消订单并释放库存。用消息队列的延迟消息可以很好地解决这个问题。

用户下单时发送一条延迟消息,比如30分钟后投递。消费者收到消息后检查订单状态,如果仍未支付就执行取消操作。

4.2 数据同步

用户信息在用户服务中维护,但订单服务、营销服务也需要用到用户的基本信息。用户服务在数据变更时发送一条消息,其他服务消费消息后更新自己的本地数据副本。

这种方案避免了服务间频繁的实时调用,也降低了核心服务的负载。

4.3 日志与监控

每个服务的日志、埋点数据通过消息队列统一汇总,再由专门的日志处理服务写到 Elasticsearch 或其他存储中。生产者只管发,不用关心日志怎么写,不会因为日志系统慢了影响业务。

4.4 分布式事务(事务消息)

以下单扣减积分为例:订单服务创建订单后,需要通知积分服务增加积分。用普通消息的问题是:如果订单创建成功但消息没发出去,积分没加;如果订单创建失败但消息发出去了,积分加了不该加的。

RocketMQ 的事务消息能解决这个问题:先发半消息(消费者不可见),执行本地事务(写订单),本地事务成功则提交消息(积分服务此时才能看到),失败则回滚消息。


五、引入消息队列后的新挑战

消息队列解决了同步调用的问题,但也带来了新的复杂度。

5.1 消息丢失

消息从生产到消费经过三个环节:生产者发送到 Broker、Broker 存储、Broker 投递给消费者。每个环节都可能丢消息。

解决方案是分环节保障:生产者端使用同步发送或事务消息确保消息到达 Broker;Broker 端配置持久化和多副本;消费者端先处理业务再确认消费,确保消息不会因为消费中途宕机而丢失。

5.2 重复消费

网络抖动可能导致消息被重复投递。消费者收到消息,处理完还没来得及确认,网络断了,Broker 以为没投递成功,又发一次。

解决方案是消费端做幂等处理:利用业务唯一标识(如订单号)判断消息是否已处理,或者用 Redis 记录已处理的消息 ID,设置合理过期时间。

5.3 消息积压

消费者处理速度跟不上生产者发送速度时,消息会在队列中越堆越多。常见原因包括消费者能力不足、下游数据库慢、或者消费者本身出了问题。

解决方案包括:临时增加消费者数量、优化消费逻辑(如改为批量处理)、或者排查并修复消费者故障。

5.4 顺序消息

某些场景要求消息按顺序处理。比如订单创建消息必须在订单支付消息之前到达,否则逻辑上说不通。

普通队列不保证顺序。RocketMQ 的顺序消息要求把同一订单的消息发到同一个队列,消费者用单线程消费该队列。


六、消息队列与 SpringBoot 的整合

SpringBoot 对消息队列提供了很好的支持,主要通过 spring-boot-starter-amqp(RabbitMQ)或 spring-boot-starter-rocketmq 来集成。

整合后的使用模式通常是:

  • 配置消息队列的连接信息

  • 定义消息的发送者和监听器

  • 发送方注入消息模板,调用转换并发送方法

  • 消费方使用注解标记监听方法,指定队列名称

SpringBoot 的消息模板封装了连接管理、序列化、异常处理等细节,让开发者只需要关注业务消息本身。


七、什么时候该用消息队列

适合引入消息队列的场景:

  • 服务间需要解耦,不希望一个服务的变动影响多个调用方

  • 存在流量波峰,需要削峰填谷保护下游系统

  • 非核心逻辑可以异步处理,不需要实时返回结果

  • 需要最终一致性,可以接受短暂的不一致窗口

不需要引入消息队列的场景:

  • 服务间调用简单,只有一两个消费者

  • 对实时性要求极高,不能接受异步延迟

  • 团队运维能力有限,多一个中间件多一份维护成本


八、总结

消息队列是微服务架构中实现异步通信的核心组件。它的价值可以概括为四个关键词:解耦、削峰、异步、最终一致。

但引入消息队列不是没有代价的。它带来了消息丢失、重复消费、顺序保证等新问题,需要生产端、Broker、消费端多方配合解决。

是否使用消息队列,取决于业务场景。同步能解决的问题不用异步,简单的调用关系不用消息队列。但当系统规模变大、依赖变复杂、流量有波峰时,消息队列是必不可少的治理工具。

相关推荐
掘金泥石流22 分钟前
上下文即服务:AI 应用的竞争,为什么会从入口转向 Context?
人工智能·架构·agent
掘金泥石流27 分钟前
AI 都能写代码了,为什么企业还需要 FDE?
人工智能·架构·agent
kisshyshy38 分钟前
从Props透传到自定义Hook:系统梳理React跨层级通信与逻辑复用
前端·架构·代码规范
躺柒1 小时前
读数据架构知识体系指南01关系数据仓库(上)
数据仓库·架构·数据分析·spark·数据湖·关系数据库·企业数据仓库
程序员清风2 小时前
Java 后端如何接入大语言模型
java·spring boot·架构·aigc
liulilittle2 小时前
mock 框架架构
ai·架构·自动化·llm·mock·测试·tools
mldong3 小时前
一套审批流要写多少代码:13 个框架的接入 diff 我数了一遍,最少 422 行,最多 1798 行
后端·架构
GreenTea11 小时前
GrokBot 核心成员 Lauren Tan:每月交付 2000 个 PR 的人,是怎么用 AI 的
前端·后端·架构
Hrain-AI11 小时前
多 Agent 并行不打架:worktree 隔离与反馈回流落地(附脚本)
网络·数据库·人工智能·架构
闲云野鹤在人间18 小时前
docker 入门 | 第7章 容器监控 和 第8章 容器日志 详解
运维·docker·容器·架构·云计算