上周三凌晨三点。
手机在枕头边震得快要掉下来。
Kafka积压告警,红色的,标的是1200万条未消费消息。
我摸黑点开手机的时候,企业微信的群消息已经刷了上百条,财务的、运营的、客服的,全在问为什么支付到账延迟了快一小时。
说实话,做了快7年后端,这种场面我见过不下十次,但每次看到那个红色的积压数字,后背还是会冒冷汗。
别上来就盲目加机器
刚工作那会我也傻,一看到积压告警,第一反应就是把consumer实例从3个开到30个。
结果呢?
上次团队里的实习生小周,看到告警直接在控制台点了扩容,开了20个consumer实例,没到5分钟,下游的MySQL连接数直接爆了。
锁表。
整个订单系统直接瘫痪了半小时,比积压本身造成的影响还大。
真的,很多人对积压的第一反应就是扩容,完全不管链路哪里出了问题。你连积压的原因都没摸清楚,加再多机器都是给系统添乱。

排查得按优先级来,别瞎折腾
我们后来踩了无数坑才总结出来,排查积压根本不需要翻一堆复杂的日志,按顺序捋三个地方就行。
先看生产端的流量曲线。
很多时候根本不是消费端慢了,是生产端突然来了一波流量洪峰。比如上次我们遇到的那次积压,是运营搞了个限时秒杀活动,本来预估峰值QPS是1万,结果实际冲到了8万,生产端10分钟就往队列里塞了平时一天的消息量。
我上次就犯过傻,盯着消费端的日志翻了12分钟,找了半天没发现异常,最后才看到生产端的流量曲线直接飙到了天花板。那12分钟,积压量从800万涨到了1500万,事后被leader拍了一下肩膀,那一下真的疼。
再翻消费端的异常日志。
是不是有什么消息格式不兼容,导致consumer一直报错重试?是不是最近发版改了逻辑,消费一条消息要花原来的3倍时间?这些都是常见的坑。
最后才轮到中间链路的网络排查。
比如机房的交换机是不是出问题了,consumer和Broker之间的网络延迟突然从1ms涨到了100ms,这种情况虽然少见,但遇到了就是大问题。
记住,告警期间每一分钟都值钱,别上来就钻牛角尖翻无关的日志。

扩容不是越多越好
很多人觉得扩容就是把实例数往上拉就行,其实根本不是。
就拿Kafka来说,每个分区同一时间只能被一个consumer实例消费,如果你队列的分区数是32个,那你开33个consumer实例,多出来的那个根本拿不到消息,只会空转占资源。之前我们就干过这种傻事,开了60个consumer,结果只有32个在干活,剩下28个把宿主机的CPU占满了,影响了上面跑的其他服务。
而且扩容也要看瓶颈在哪里。
如果是生产端流量太大,你光加consumer没用,消费的速度赶不上生产的速度,积压只会越来越多。这时候得先给生产端加限流,把非核心的消息先降级,比如什么短信通知、埋点上报的消息,先临时塞到备用队列里,等核心链路的积压清完了再慢慢处理。
如果是下游的数据库有瓶颈,比如写库的TPS最多只能到2000,那你加再多consumer也没用。这时候要先优化下游的写入逻辑,比如原来一条一条写库,改成100条一批批量写,TPS直接就能翻10倍,比你加10个consumer有用多了。
扩容的前提是链路没有单点瓶颈,否则就是给系统添乱。
那次周三的告警,我们最后是这么处理的:
先给生产端加了2倍的限流,把非核心的通知类消息先降级到备用队列。
然后把consumer的批量拉取大小从100调到500,下游写库从单条改成200条一批。
整个链路的消费速度从原来的每秒1200条,直接提到了每秒18000条。
花了15分钟,把1200万的积压全部清完。
说实话,那次之后我们把这套步骤写成了runbook,贴在运维群的公告里。之后再遇到积压,从来没超过半小时处理完。