首页 / m.itcdj.com / 正文

消息队列不是流量救火队

上周跟一个做电商的技术负责人吃饭,他说刚花三周把Kafka从系统里拆了。我当时筷子都停了。

很多人对消息队列的印象,就是高峰期扛流量的万能缓冲垫。接口慢了加一层,服务挂了加一层,连要发个通知都得往队列里塞。但真用久了才发现,这东西带来的麻烦,有时候比解决的问题还多。

不是所有异步需求都要上队列

我见过太多团队,刚接触消息队列的时候,恨不得把所有的调用都改成异步。用户点个赞要发队列,后台改个配置要发队列,甚至连定时清理日志都要往队列里塞任务。说白了就是觉得,异步就是高级,加了队列就是架构升级。

但你算过成本吗?消息队列的核心成本不是部署,是后续的全链路治理。你要处理消息重复消费,要处理消息丢失,要处理顺序问题,还要做积压监控、扩容预案。如果你的系统本身日活不过几万,下游依赖的短信、积分服务本身都很稳定,那用个简单的线程池做异步任务,十分钟就能搞定的事,干嘛要花几周搭一套消息队列集群?

真要上队列的场景其实很明确:下游依赖多,且每个依赖的耗时都不稳定,同步调用会拖慢主接口的响应时间。比如用户下单之后,要发短信、加积分、推优惠券,这三个动作哪怕某个接口临时卡两秒,用户也不该等这两秒才看到下单成功的页面。这种时候上队列,把非核心流程拆出去,才是划算的。

业务接口同步异步调用链路对比图

积压的坑比你想的多

提到队列的坑,第一个绕不开的就是消息积压。这事说起来真的是血泪教训。有个做本地生活的团队,去年国庆做核销活动,高峰期往队列里灌了近百万条通知消息,消费者那边因为第三方短信接口限流,半小时就堆了三十万条没消费。

他们第一反应是加消费者实例,从5台加到20台。结果呢?第三方短信接口直接被新增的调用量打挂,队列里的消息堆得更快了,甚至还有消费者因为没做好幂等,重复给用户发了好几条核销成功的短信,投诉爆了好几天。

很多人做容量评估的时候,只算自己的服务能扛多少QPS,完全忘了下游最脆弱的那个依赖。队列的消费能力上限,从来不是由你的消费者实例数量决定的,是由下游最脆弱的那个依赖决定的。你给消费者加再多机器,下游接口扛不住,该堆还是堆。

更麻烦的是,积压久了还容易出连锁问题。比如你为了快速清积压,把消费的过滤逻辑临时关了,原本该只发给下单用户的消息,全推给了所有历史用户。换谁不冒冷汗?

消息队列消费积压链路排查示意图
消息队列消费积压链路排查示意图

别把队列当数据仓库用

还有个常见的误区,是觉得消息队列存消息很方便,就把所有的业务事件都往里面塞,存个七八天甚至十几天,出问题了就回去翻消息。说实话,我见过最离谱的操作,是有个小团队把RocketMQ的消息当订单流水用,要核对某笔订单的状态,就拉取这个订单ID的所有消息遍历。

这种操作短期看好像省了建存储的事,长期代价大到你想不到。你用的是Kafka还好,毕竟它天生就是靠磁盘顺序写扛高吞吐的,但你存的消息越多,集群的磁盘成本越高,要是哪天要回溯一个月前的消息,整个集群的性能都会被拖垮。要是用的RabbitMQ,存久了直接会触发磁盘告警,严重的还会导致队列拒绝写入。

消息队列的定位是传输通道,不是持久化存储,更不是业务数据的查询入口。如果你真的需要留存业务事件做后续分析、问题排查,那应该把消息同步到数仓或者专门的事件存储里,而不是靠队列本身扛存储的压力。

至于大家常纠结的选型问题,这事没那么玄。做日志采集、大数据同步,Kafka的吞吐足够,生态也全。做业务场景的事务消息、延迟消息,RocketMQ的能力更贴合。要是你只是做个小系统的异步通知,RabbitMQ的上手成本低到你一下午就能搭完。别听那些人吹什么必须上XX,适合自己的业务量级才是最重要的。

上周那个拆Kafka的负责人跟我说,他们现在把原来的队列换成了简单的内存异步任务,日活涨了三倍都没出过问题。毕竟技术工具从来没有高低之分,能解决当下的问题,还不给未来留坑的,就是好选择。