秒杀开始那一秒,十万个下单请求同时涌进来。如果每个请求都直接写数据库,系统大概率当场趴下。消息队列干的事,就是把这些请求先接住、排好队,再让后端按自己的节奏慢慢处理。
系统设计科普系列第15期把消息队列的核心价值拆成了三块:解耦、削峰、异步。本质上是把一次同步调用,变成一条可持久、可重放的异步事件流。
解耦、削峰、异步,各管一段
解耦让上下游服务互不阻塞。下单服务只管把消息丢进队列,至于后面是发短信、扣库存还是记积分,它不关心,也不等待。
削峰则是把瞬时的高并发流量平滑消化。请求在队列里排队,后端按自己的处理能力消费,不会被瞬间打垮。
异步带来的收益更直接:主流程链路的响应延迟被显著缩短。用户不用等所有下游动作跑完,就能拿到结果。
Kafka的有序性,只在单个Partition里成立
很多人以为Kafka能保证消息全局有序,这是个常见误解。Kafka的有序性严格限定在单个Partition内部。
实际做法是:把同一个key路由到相同分区,就能保证这批消息的局部时序。比如同一个订单的操作,只要key一致,顺序就不会乱。
但如果非要追求全局有序,就必须依赖单分区。代价也很明确——整体吞吐量会下降。这是一个典型的取舍,不是配置问题。
三种投递语义,默认那个最需要小心
消息投递语义分三种:
- at-most-once:最多投递一次,可能丢消息
- at-least-once:至少投递一次,可能重复
- exactly-once:恰好一次,实现成本最高
at-least-once是工程上的默认选型,但它强制绑定了一个前提:消费者必须做幂等设计。重复消费如果没有幂等保障,会产生严重的业务副作用——比如重复扣款、重复发货。
极端情况下失败的消息,需要通过死信队列(DLQ)隔离出来,避免它一直卡在分区里阻塞后续消息。
选型没有标准答案
Kafka、RabbitMQ、Pulsar各有适用场景。选型时要考虑的是吞吐量需求、有序性要求、投递语义的严格程度,以及团队对这套系统的运维能力。没有哪个是万能解。
热门跟贴