一个下单接口,测下来平均 1.2 秒,慢的时候到 2 秒多。拆开看,真正写订单表只花 100 多毫秒,剩下时间全在等三件不急着要结果的事:发确认短信、往企业微信群推一条、写一份详细日志。
拆法
接口里只留两件:写订单、往 RabbitMQ 投一条消息。消息体很小,就 order_id 加一个事件类型。剩下三个动作各一个消费者,自己取、自己重试。
改完接口平均 200 毫秒出头。用户点完立刻看到成功页,短信晚个一两秒没人察觉。
1. 没开持久化,重启丢了 6 条
第一版图省事,队列和消息都用的默认配置。有天晚上服务重启,队列里 6 条待发消息没了,6 个用户没收到确认短信。查日志才发现的,用户其实没投诉。
改法:队列 durable、消息 delivery_mode 设 2、生产者开 confirm。三样一起才算稳。交换机那边也标了持久化。
2. 消费者没做幂等,短信发了两遍
有个消费者处理完业务逻辑才 ack,中间网络抖了一下没 ack 上,消息重新投递,短信发了第二次。用户收到两条一模一样的。
后来把 ack 提前到业务之前?不行,会丢。改成业务里按 order_id 加 event_type 做去重表,插入成功才真发,重复的直接 ack 掉。跟积分那套幂等键是一个思路。
3. 死信队列
一直失败的消息不能无限重试,会把队列堵住。设了最多 3 次,超过进死信队列,同时在监控里计数,超过 5 条就告警。运维值班的表上多了一行「看死信长度」。
4. 顺序不保证
企业微信那条提醒偶尔比订单成功的日志还早到,看着别扭但不影响什么。我们没强求顺序,在消息体里带了 occurred_at,展示时按这个时间排。为了顺序上单分区队列,这笔账不划算。
后来
三个消费者现在还能单独停。有回短信服务商故障,我们把那个消费者停了两小时,下单照常,恢复后一起补发。搁以前,短信一挂,整个下单跟着卡。
那 6 条丢掉的消息后来是照着订单表补发的,补完在文档里记了一笔:默认配置不能信。

