云计算、AI、云原生、大数据等一站式技术学习平台

网站首页 > 教程文章 正文

线上问题之:RocketMq重复消费(rocketmq如何保证消息不被重复消费)

jxf315 2025-03-25 14:36:55 教程文章 22 ℃

在怀疑是系统(服务器)问题前,先多考虑是否是程序本身的问题

大过年在家里,正玩王者,突然收到带的新人徒弟发来的微信消息,说线上mq数据统计错乱了,有的多,有的少,说可能RocketMq集群有问题。影响到了用户报表,十分紧急,无奈之下,只能坑队友一把(挂机),连上easyConnect,帮徒弟定位问题。

刚上线,就看到徒弟已经拉了一个群,里面拉了运维同事,让运维紧急看下mq消息错乱,出现线上问题了,运维小哥十分委屈的说不太可能,mq跑几年了也从来没出现这个问题,一定是用的有问题或者数据本来就是如此也不是不可能。两个人一直在争吵,推诿。。。

功能简介:这个功能是一个消息量统计功能,类似于:统计每个用户在我司app上的发言量(窗口统计,每分钟,每10分钟等等这样)。

定位过程:

1、我心想,mq不太可能出现这种低级问题。会不会是app端上报重复(上报漏)了?于是我先自己验证下,自己登录app发言一次后,发现统计量确实「是2」。然后看nginx日志,发现app侧「只调用了一次」。排除了app的问题(暂时先不管统计少的问题,这时已经发现端倪了)

2、怀疑写入mq写了2次?是这样的,服务收到app请求后,做简单业务处理后,丢入mq,供下游各种程序进行消费做业务(解耦)。然后问了其他业务,别人数据是正常的,没有出现翻倍;并且开debug看了下日志,写入mq只有一次。

3、极有可能是徒弟的消费统计程序有问题。看了徒弟的统计逻辑代码,看了好久没看出破绽,百思不得其解,无意间看了眼consumer初始化代码,终于发现问题了!!!徒弟初始化mq是这样的:

问题就在这里,由于徒弟的程序是多实例部署在一台ecs上的,在一台ecs上启动了2个消费者(jvm)进程,如果此时设置了固定的instanceName,则会造成「部分消息重复消费」「部分消息丢失!」,mq底层关键代码如下:



可以看到,分发queue的凭据是cid(下面那一坨算数运算先不care),凭直觉,各位是不是已经感觉到了,如果cid相同,那铁定是消费相同的queue,那不就消费了2次嘛,好了,上证据:



可以看到,cid的逻辑是:ip+${instanceName}+${unitName},而如果不设置instanceName,则底层默认用jvm的pid。unitName一般不会设置。这样各位就看清楚了吧,就是因为徒弟设置了就造成了固定的instanceName(见图1,KTJ_user_msg_stats),所以他的两个消费者的cid是相同的,如:127.0.0.1@KTJ_user_msg_stats,所以在给消费者分派queue时,这2个消费者被分到了相同的queue。造成了消息重复。

问题到这里还没有结束,为什么会有消息丢失?顺水推舟呀,见图2,本身这个topic有4个queue,2个消费者消费了相同的队列,剩余2个队列没有被消费(没有被分派消费者),所以有2个队列的消息丢失!(看mq的lag监控也可以看到,lag一直在上涨!)

解决措施:删除设置instaceName的代码,重启服务,发现统计正常。当然,也可以设置全局唯一的instanceName。

复盘总结:出现问题时,特别是依赖了中间件时,不要上去就把“皮球”踢给中间件(中间件不背锅)大多数还是要检查自身的使用姿势是否正确。不是说中间件不会有问题,而是这种低级别的问题,早就被官方fix了。假设您真的发现了中间件问题,不要犹豫,去git提issue,说不定还能“出个名”!

思维发散:如果徒弟的代码是在不同的机器上启动,会不会有这个问题?为什么,评论区见

好了,打王者去了。

最近发表
标签列表