做业务系统到一定规模,几个老问题基本绕不开:下单高峰时数据库吃不消,支付回调偶尔丢失,未支付订单还得靠定时任务扫表关单。这些问题大多源于同步调用链过长,服务之间耦合得太紧,消息队列就是为拆开这类耦合而用的。我们这两年用阿里云MQ比较多,下面结合三个场景,说说具体怎么用,以及踩过的坑。

阿里云MQ大致分哪几类
阿里云MQ目前常见的产品形态有三种:基于RocketMQ的消息队列 RocketMQ版,面向大数据和日志场景的消息队列 Kafka版,以及面向物联网设备的消息队列 MQTT版。思路基本相同:生产者把消息发到Topic,消费者用Group ID订阅消费,存储、投递和失败重试交给服务端处理。选哪一个,主要看消息从哪里来。
开始配置前,先搞清楚三个概念,后面很多问题都能对上号:
- Topic:消息的分类。不同业务线的消息分开建Topic,监控和扩缩容才能各管各的,一个业务出问题也不会拖累其他看板。
- Group ID:消费者分组。同一组里的多个实例分摊消息,每条消息只会被组内某个实例处理;不同组则各自完整消费同一份数据,互不影响。
- 消息类型:普通消息、顺序消息、定时与延时消息、事务消息。选型要看业务语义,比如库存变动要求顺序,就不能图省事只用普通消息。
场景一:大促时把下单链路拆轻
大促前我们压测下单接口,发现数据库写入能力撑不住预估峰值。下单主链路本身要尽量短,积分发放、站内通知、数据看板写入这类动作完全可以异步化。用户下单成功后,只向RocketMQ版的Topic写一条消息,各消费端按自己的处理能力慢慢拉取。下单接口的响应时间明显变短,数据库压力也平稳了不少。
但削峰不等于消息可以无限堆积。压力只是从接口挪到了队列里,堆积到一定程度照样会出事。消费端的并发数要根据数据库和下游接口的承受能力来定,同时给堆积量设置告警,超过阈值就及时扩容或限流。
场景二:未支付订单的自动关闭
未支付订单要在一段时间后自动关闭。早期不少项目用定时任务全表扫描,数据量上来以后扫描越来越慢,还容易漏单。改成延时消息之后,逻辑反而简单:下单时同步发送一条延时消息,到期由消费端查一次订单状态。仍未支付就关单,已支付就直接忽略。整个过程不再依赖全表扫描,也不用维护一堆时间索引。
有一点要提前确认:延时消息支持的时长范围和产品版本有关。上线前请以对应版本的官方文档为准,不要照搬旧项目里的配置,否则很容易在上线后才发现时间不对。
场景三:支付回调与事务消息
支付成功后,系统要更新订单状态、扣减库存、通知物流,这几步必须保持一致。最容易出问题的顺序是先改订单,再发消息。只要中间服务宕机,就会出现订单已经支付、消息却没发出去的情况,下游永远等不到这条消息,对账时才会发现。
事务消息就是为这个场景准备的。先发一条半消息,本地数据库事务执行成功后再提交投递,失败则回滚。如果中间状态不明确,服务端会回查本地事务的结果来决定是否投递。这样本地操作和消息发送之间就能保持最终一致。
不过消费端仍然要做幂等。网络超时和重试都可能让同一条消息被投递多次,重复扣库存或重复发货的代价不小。常见做法是用订单号加业务类型作为唯一键,处理前先查记录,已经处理过就直接返回成功。
两个容易踩的误区
第一个误区,是把消息队列当成万能的解耦工具。消息异步化之后,一笔订单可能要经过好几个消费端才算完成,排查问题时调用关系和数据流向都看不见,只能靠链路追踪和完整的日志去还原。没有这些配套,出了问题很难定位。
第二个误区,是默认消息只会到达一次。队列通常保证的是至少投递一次,也就是说重复是正常情况。业务层必须自己处理幂等,不能指望中间件替你兜底。
选型与上线前的检查
- 先判断业务是否真的需要顺序、事务或延时能力,再选消息类型。只为了用新功能而套复杂方案,维护成本会一直压在团队身上。
- 消费端做幂等,可以基于订单号、流水号这类业务唯一键去重。这一步在测试环境就要验证,别等线上重复扣款了才补。
- 为多次消费仍失败的消息配置死信队列,并定好谁来人工处理、怎么重新投递,避免消息悄悄躺在那里没人管。
- 监控堆积量、消费延迟和失败率,关键指标配置告警,并定期演练故障场景,比如消费端宕机、网络抖动时系统的表现。
- 测试和生产使用独立的命名空间与访问账号,防止测试消息误入生产链路。
阿里云MQ解决的是系统之间怎么协作得更松、更稳的问题。用对了,大促时接口不容易被压垮,订单不丢,对账也好做。用错了,只会多出一层很难排查的复杂度。动手之前,先把每条消息失败时怎么兜底想清楚,通常比急着接入更有用。