当前位置: 首页 > 科技观察

RocketMQ消息丢失如何排查?

时间:2023-03-12 16:38:25 科技观察

短信丢失怎么办?我们在使用mq的时候,经常会遇到消息消费异常的问题。原因有很多,比如:producer发送失败,consumer消费异常,consumer根本收不到消息“那怎么办?”你检查了吗?”其实RocketMQ-Dashboard可以用来高效检查,还有很多你想不到的功能。搜索,根据消息id,时间等,“Themessagewasnotfound?”表示producer发送异常,或者消息可能已经过期,因为rocketmq消息默认保存72小时,而你这时候可以去producer端的log进一步确认,“找到消息了!”然后查看消息的消费状态,如下图,消息的消费状态为NOT_ONLINE。NOT_ONLINE是什么意思?”别着急,我们一步步来分析,看看TrackType有多少种状态publicenumTrackType{CONSUMED,CONSUMED_BUT_FILTERED,PULL,NOT_CONSUME_YET,NOT_ONLINE,UNKNOWN}每种类型的解释如下:类型说明CONSUMED消息已被消费CONSUMED_BUT_FILTERED消息已投递,但过滤PULL消息的消费方式为拉取方式NOT_CONSUME_YET当前未消费NOT_ONLINECONSUMER不在线UNKNOWN未知错误“如何判断消息已被消费?”我们在上一节中提到,broker会用一个map来保存每个queue的消费进度。“如果队列的偏移量大于查询到的消息的偏移量,则消息将被消费。消费,否则不消费”(NOT_CONSUME_YET)。在RocketMQ-Dashboard上,我们其实可以看到每个queue的offset(broker位置)和消息消费的offset(consumer位置)。不同的是还没有被消费的消息。当消息全部消费完后,差异为0,如下图:“CONSUMED_BUT_FILTERED表示消息已经送达,但是被过滤掉了”。比如生产者发送topicA和tagA,而消费者订阅topicA和tagB。“CONSUMED_BUT_FILTERED(消息已发送但已过滤)是如何发生的?”这就不得不提到RocketMQ中的一个概念,“消息消费必须满足订阅关系的一致性,即一个consumerGroup中所有消费者订阅的Topic和tag必须一致,否则消息会丢失。”下图场景,发送4条消息,consumer1订阅topica-taga,consumer2订阅topica-tab,consumer1消费q0中的数据,consumer2消费q1中的数据,对于msg-1和msg-3投递到q0,只有msg-1可以正常消费,而msg-3是CONSUMED_BUT_FILTERED,因为msg-3投递到q0,但是consumer1没有消费tagb的消息,所以消息被过滤,消息丢失.同样,消息msg-2也会丢失,“注意,还有一个很重要的点”!虽然消息消费失败,但是消息的offset还是会正常提交,即“消息消费失败,但是status也会被CONSUMED”。“RocketMQ认为消息消费失败需要重试的场景有哪些?”返回ConsumeConcurrentlyStatus.RECONSUME_LATER返回null主动或被动抛出异常“那失败的消息去哪了?”当信息消费失败,会放入重试队列中,主题名称为%RETRY%+consumerGroup。“消费者还没有订阅这个主题,怎么消费重试消息呢?”其实在consumer启动的时候,framework内部给你订阅了这个topic,所以retrymessage是可以消费的。“另外,消息并不是一直重试,而是每隔一段时间重试一次。”前几次重试和最后一次重试之间的间隔前几次重试和最后一次重试之间的间隔110秒97分钟230秒108分钟31分钟119分钟42分钟1210分钟53分钟1320分钟64分钟1430分钟75分钟151小时86分钟162小时主题名称为%DLQ%+consumerGroup。“所以当你发现消息状态为CONSUMED,但是消费失败时,就在重试队列和死信队列中寻找。”这个问题的背景是我们有两个系统,中间使用mq来保证数据的一致性。结果哪天数据不一致。肯定是消费者消费消息有问题,或者是生产者发送的消息有问题。先根据时间段找到消息,确保发送没有问题,然后查看消息状态为NOT_CONSUME_YET,说明消费者在线但没有消息。“NOT_CONSUME_YET表示消息还没有被消费”,但是距离消息发送已经很久了。消费者不应该消费它。查看日志,消费者确实没有消费过。使用RocketMQ-Dashboard查看broker和consumer站点,0队列正常消费,其他队列没有消费。”感觉这个负载均衡策略有问题,为什么0队列有那么多消息,其他队列怎么没有消息?问了一波中间件同学,是他们改了负载均衡策略再次?”果然,变了!测试环境下,使用队列纬度来区分多个环境,0为基准环境,我们团队还没有使用过多个环境,所以发送和接收消息都会在队列0上,其他队列不会使用(“你可以简单的认为测试环境发送和消费消息只会使用0队列”)。“那问题来了!”首先,消息的状态是NOT_CONSUME_YET,也就是说消息肯定已经投递到0队列外了,但是中间件小伙伴说消息不会投递到0队列。为了验证我的想法,首先需要证明没有被消费的消息确实投递到了0队列以外的队列。中间走的弯路就不说了,直到看了RocketMQ-Dashboard的源码,“发现Dashboard其实返回了很多消息的信息,只是页面上没有显示,只是看界面返回。”乖乖,发现了一个新世界,消息的所有属性都在这里,看到queueId是14,果然验证了我的想法。看bornHost其实就是我们办公室的网段。“本地启动的负载均衡策略和测试环境的负载均衡策略不一样吗?”本地调试了一波代码,发现本地生产者会向所有队列发送消息,消费者也会消费所有队列的消息。“至此,问题已经找到了!”生产者在本地启动了一个服务,注册到测试环境的zk,测试环境的一些请求发送到本地,向0队列以外的队列发送消息,但是测试环境的消费者只会Consume0号队列的消息,导致消息长时间没有被消费。

猜你喜欢