景区票务系统的消息队列架构演进:从订单到对账的异步化改造实录|中科智旅景游宝

景区票务系统的消息队列架构演进:从订单到对账的异步化改造实录|中科智旅景游宝

先说个让人头大的场景。每年五一、国庆,售票高峰期订单服务同步干的事特别多:创建订单、扣库存、发短信、发模板消息、更新会员积分、推送给 OTA 渠道、写财务流水。一个下单接口里串了 6 个逻辑,游客点一次购买,页面要等 2 到 3 秒才出结果。更恐怖的是,短信服务一抖动,整个下单流程跟着超时,游客以为支付失败,又下了一单------重复下单就是这么来的。

后来我们把下单链路做了异步化改造,核心就是引入消息队列(MQ)。今天把这次改造的完整过程讲清楚,从选型到落地到踩坑。

一、先理清:哪些逻辑该异步?哪些必须同步?

改造第一件事不是选 MQ,而是梳理同步/异步边界。判断标准很简单:游客付完钱,哪些结果是他立刻需要知道的?

必须同步的(游客感知的核心链路):

  • 创建订单、锁定库存
  • 调支付、收支付结果
  • 返回订单号、支付状态给游客

必须异步的(游客不感知、但系统要做的事):

  • 发短信、发微信模板消息(通知类,晚几秒无所谓)
  • 更新会员积分(可以后补)
  • 推送订单给 OTA 渠道(渠道对账可以延迟)
  • 写财务流水、生成对账数据(财务可以日终对账)
  • 更新数据驾驶舱的经营指标(本来就是准实时)

二、MQ 选型:Kafka 还是 RocketMQ?

市面主流就几个,我们对比后选了 RocketMQ,理由很实际:

对比项 Kafka RocketMQ RabbitMQ
吞吐量 极高 高 中
消息可靠性 高(需配置) 高(自带事务消息) 高
事务消息 不支持原生 支持原生 需插件
延迟 毫秒级 毫秒级 微秒级
运维复杂度 中 中 低
中文文档 一般 丰富 丰富

关键差异在事务消息:景区票务里"订单落库 + 发消息"要保证原子性,RocketMQ 原生支持事务消息,代码量少很多。加上团队之前没碰过 Kafka,学习成本也是考量。最终定了 RocketMQ 4.9。

三、改造过程:核心链路怎么拆?

1. 下单接口:同步只剩三步

改造后的下单流程:

复制代码
游客点击购买
  → 创建订单(本地事务,落库)
  → 锁定库存(Redis + DB)
  → 调支付接口,返回支付二维码
  → 接口结束(响应时间从 2.5s 降到 400ms)

支付成功回调后,才把"后续动作"发到 MQ:

  • ORDER_PAID 消息 → 发短信、模板消息
  • ORDER_PAID 消息 → 更新会员积分
  • ORDER_PAID 消息 → 推送 OTA 渠道
  • ORDER_PAID 消息 → 写财务流水

一个支付回调,拆成 4 个 topic 的异步任务,全部并行消费。

2. 消息可靠性:事务消息 + 重试 + 死信

异步化最怕丢消息,我们做了三层保障:

第一层:事务消息保证"业务和消息同生共死"

RocketMQ 事务消息的核心流程:先发半消息 → 本地事务执行成功 → 提交消息 → 消费者才可见。如果本地事务失败,半消息被回滚,消息也不会发出。这就保证了"订单落库成功,消息一定发出"。

第二层:消费失败重试

消费端处理失败自动重试(RocketMQ 默认重试 16 次,指数退避)。短信服务抖动导致消费失败,重试机制兜底,不会丢。

第三层:死信队列 + 人工补偿

重试 16 次还失败的消息进死信队列,我们有个补偿 Job 每小时扫一次死信,人工介入处理。财务流水这种关键消息,死信队列里绝不能躺过一天。

3. 消费幂等:上篇方案的复用

消息消费必须幂等(前面幂等篇讲过,这里直接复用同一套):消费端按消息 ID 去重,重复投递不重复处理。OTA 渠道推送这种场景,渠道侧可能重复回调,幂等键必加。

四、踩过的坑(血泪教训)

  1. 消费端业务代码太厚:第一版把"发短信 + 更新积分 + 推渠道"写在一个消费者里,一个环节报错整条消费失败重试,其他环节跟着重复执行。后来一个 topic 一个消费者,职责单一,各重试各的。
  2. 消费顺序错乱:OTA 渠道要"先创建后取消"的顺序,默认并发消费会乱序。改成单消费者 + 顺序消息(按订单号 hash 到同一队列),顺序问题解决。
  3. 消息堆积没监控:某个节假日渠道推送消费者挂了 2 小时,消息堆积了几十万条,没人发现。后来给每个 topic 加了"堆积量 > 1 万条告警",再没出现过这种事。
  4. 重试风暴:下游全挂时,重试全部在同一时间点打过去,把下游打得更死。改成指数退避 + 随机抖动,重试错开。
  5. 事务消息半消息积压:事务消息如果本地事务执行超时(比如 DB 抖动),半消息会一直挂着,直到超时回查。回查次数要配置合理,不然半消息堆积影响性能。

五、FAQ 快答

Q:为什么不直接用 HTTP 异步回调? HTTP 回调没有重试、没有削峰、没有消息堆积管理。MQ 的价值不只是异步,还有削峰填谷和可靠性保障。高峰期 1 万笔订单的短信、积分、渠道推送,靠 HTTP 回调并发去打下游,下游直接被打垮。

Q:RocketMQ 和 Kafka 到底怎么选? 需要事务消息、团队不熟 Kafka → RocketMQ;追求极致吞吐、消息可以接受一定丢失风险 → Kafka。景区票务对消息可靠性要求高,我们选 RocketMQ 更合适。

Q:异步化后对账怎么做? 异步逻辑最终都要落库,日终对账任务按订单号聚合所有异步结果,对比"订单状态 vs 各环节结果",差异项自动标记人工处理。异步化没有改变对账逻辑,只是把对账数据的产生从同步改成了异步。

Q:小团队用 MQ 会不会过度设计? 单机版业务确实不需要。判断标准:同步链路里有没有"非核心但耗时"的逻辑(短信、通知、积分)?有没有高峰削峰需求?有就用,没有就先别上。


最后说点实在的。消息队列不是银弹,它是把同步链路里的非核心逻辑拆出去的工具 。中科智旅景游宝这次改造最直观的结果:下单接口响应从 2.5 秒降到 400ms,黄金周高峰期短信、渠道推送再没拖过后腿,重复下单问题也少了大半。核心原则就一句话------同步保体验,异步保扩展,可靠性靠三层兜底。

评论区聊聊你们系统的异步化改造,MQ 选型踩过什么坑?消息丢失、重复消费、顺序错乱,哪个最要命?

相关推荐
用户674874273341 小时前
我踩了4个坑才搞懂 AI 视频压力测试该测什么
人工智能
weiwin1231 小时前
MAF 入门(8):多模态实战——让 Agent 看懂图片、处理混合内容
人工智能·后端
code2cat1 小时前
【随笔】RAG重排怎样挑证据:先扩大候选,再把相关片段排到前面
人工智能·python·大语言模型·rag
知赛官方2 小时前
DeepSeek 又偷偷升级了:我实测了 V4.1-Flash-1017,一句话生成可玩游戏,但有个坑会吃掉你 90% 的预算
人工智能
Eric72592 小时前
职场人学 AI 的正确顺序:先学什么、后学什么
ai编程
宋艳_AI应用开发2 小时前
页面有时间,AI却说没有:排查一个Bug,搞懂了AI应用的调试逻辑
人工智能
用户610719266632 小时前
我踩了4个坑才搞懂 AI 视频模型该怎么选
人工智能
张伽蓝ErosJane2 小时前
AI Agent 平台搭建必读:Anthropic 多智能体实验的三个工程警示
ai编程
doujiaozh2 小时前
从 0 到 1:我用 300 行代码给 AI 记忆加上了“不可篡改的收据”(附开源实现)
人工智能·开源