双十一没有消息队列引发的问题:董事长把青花瓷瓶砸在了连夜赶来的架构师脚边...

95 阅读5分钟

简介:通过小故事讲解没有消息队列遇到的问题哪些问题,如数据库连接池被挤爆,超卖,消息队列是如何解决的,

1. 推荐文档

掘金消息队列概念讲解

lainChin

2. 小故事

2.1. 场景:双十一没有消息队列引发的问题

"阿良的手表指针刚跳过23:59,监控大屏的流量曲线就突然像过山车般直冲云霄。'每秒十万次点击,还在持续增长!'值班实习生小王的声音已经变了调。【批注:双十一请求激增,是正常场景】

'启动备用服务器组,把负载均衡权重调高!'阿良扯松领带,后颈渗出的冷汗把显示器蓝光折射成细碎光斑。他分明记得三天前压测时,这套新架构能轻松扛住十五万QPS。【批注:常规应急措施,符合SRE手册的标准流程】

00:01分,第一个异常参数跳进视野——内存占用率95%。未读告警开始以每秒三十条的速度堆叠。阿良突然意识到什么,抓起内部通讯器大吼:"促销接口没走消息队列?"【批注:未引入消息队列导致所有请求直接冲击应用层,线程阻塞引发JVM堆内存OOM前兆;每秒30条告警符合监控系统的指数告警风暴模型】

回答他的是一串刺耳的数据库超时警报。某个超时3秒的支付请求触发客户端自动重试机制,五千个相同订单在系统里疯狂复制。连接池计数器瞬间飙红,MySQL集群像被抽干氧气的鱼群集体翻起肚白。

【批注:内存瓶颈引发连锁崩溃:JVM堆内存过载导致请求处理延迟,触发客户端指数级重试;未限制的数据库连接请求突破连接池阈值,引发全局锁死锁】

"用户开始重复付款了!"财务总监撞开机房门的瞬间,阿良看到流量统计器上的数字诡异地膨胀成五十万。扩容按钮被拍下的刹那,进度条显示的90秒倒计时仿佛死刑宣判。【批注:缺乏请求冥等性校验导致重复交易;K8s扩容时Pod冷启动需加载容器镜像/注册服务,期间新增服务器无法立即分流请求】

00:03分,最后存活的Nginx节点吐出502错误码。阿良在彻底黑屏的监控大屏上,看到自己煞白的脸被无数崩溃日志的红色残影割裂成碎片。走廊尽头传来瓷器碎裂声——董事长把青花瓷瓶砸在了连夜赶来的架构师脚边。”

上面的问题本质上就是流量激增问题,有大量请求服务器处理不来,那么如果有消息队列如何处理呢?

所有促销请求先写入消息队列,可以理解为所有请求都在消息队列进行排队等待,应用层通过可控的消费者线程池逐步消化请求。队列堆积的请求不会直接压垮内存,Kafka单分区可承载10万级TPS,且消费端批量拉取降低线程竞争。

幂等性控制(支付重复请求)
消息队列内置唯一消息ID,配合数据库冥等表(request_id主键约束)实现自动去重。即使客户端重试5次,相同请求在服务端仅处理1次

消息队列处理流量削峰还有应用解耦,异步处理等功能,

3. 使用原理

双队列餐饮模型解析:

  1. 订单队列(顾客→厨师)

生产者:顾客(生成订单请求)
队列:餐厅订单窗口(缓存未处理订单)
消费者:厨师(从队列获取订单烹饪)
流程:
顾客下单 → 订单写入队列 → 厨师批量拉取3份订单 → 并行烹饪 → 完成品放置传菜队列

优势:

顾客下单后无需等待厨师响应(异步处理)
高峰期订单积压在队列中,避免厨师过载崩溃
2. 传菜队列(厨师→服务员)

生产者:厨师(产出完成菜品)
队列:传菜窗口(暂存待送菜品)
消费者:服务员(从队列取菜配送)
流程:
厨师放菜入队列 → 服务员按桌号顺序取菜 → 配送至对应顾客 → 确认送达后标记完成

优势:

厨师专注烹饪不受送餐速度制约
服务员根据配送能力弹性调整取餐频率
系统效果

解耦:顾客、厨师、服务员三方工作节奏互不影响
削峰:午间100份订单峰值被队列缓冲,厨房按50份/小时稳定处理
容错:若某厨师生病,其他厨师可从同一订单队列接替工作

4. 消息队列有哪些关键名词?

  1. 生产者(Producer) :创建/发送消息的应用程序,往一个消息队列发送消息的程序就可以说是这个消息队列的生产者
  2. 消费者(Consumer) :接收/处理消息的应用程序,往一个消息队列获取消息的程序就可以说是这个消息队列的消费者
  3. 消息代理(Broker) :消息传递中间件(如RabbitMQ、Kafka)
  4. 队列(Queue) :存储消息的缓冲区容器
  5. 主题(Topic) :发布-订阅模型的逻辑通道(如Kafka)
  6. 消息(Message):传递的信息,但是一般都有大小限制,例如kafka都默认限制是1Mb

接下来会继续介绍kafka等消息队列的相关信息,