概述
这篇文章是以同事在实际工作中遇到的问题作为分析的切入点,加深自己对mq的掌握,践行“干中学”的团队理念。
当自己差不多把基本概念都掌握的差不多的时候,必须需要实际的案例或者实践来提深自己的深度,这个时候just do it 变得很重要,所以我喜欢不停的被人挑战,截止目前帮人解答的问题包括:client端消息堆积问题、批量消息拉取问题中遇到的神奇的数字32、以及本篇的tag不一致造成的假象,也就说会有3篇文章输出。
整个mq的问题解决文章会收录在mq的另外一个《rocketMq干中学》专题当中,欢迎订阅,欢迎挑战。
背景
某次线上发布升级mq的消费端修改订阅的topic对应的tags,为了保证稳定性,采取了灰度发布策略,也就说发布一台服务后观察一段时间看是否正常再全量发布。
升级的内容为consumer订阅的tags信息,灰度一台之后存在同一个consumeGroup下有多个consumer,且其中一个consumer的topic的tags信息和其他consumer不一致。
如我们在consumeGroupA下有3个consumer,一开始3个consumer订阅了topicA + tagA||tagB,然后我们升级一个consumer订阅topicA+tagC,这个时候在同一个consumeGroup下针对同一个topic会有两个不同的订阅信息。
升级以后的现象是什么呢,重要的事情说3遍,说3遍,说3遍。
升级后我们发现所有的consumer都没有消费数据的记录!
升级后我们发现所有的consumer都没有消费数据的记录!
升级后我们发现所有的consumer都没有消费数据的记录!
复现
请按照以下顺序进行复现操作
1、启动consumeA,负责订阅orderTopic,tags为A||D;
2、等待一段时间,待consumeA启动完成;
3、producer发送消息,发现consumeA正常消费消息;
4、启动consumeB,负责订阅orderTopic,tags为F;
5、producet发送消息,发现consumeA和consumerB都没有消费记录;
6、理论上这个时候consumeA应该能够消费(tags一致),但是事实上却没有。
原因分析
在复现问题以后,基本上你知道离定位问题就不远了,其实对于经常出现的问题你只要静下心去排查问题就不大了,
我的问题排查理念:
1、对mq在订阅topic的过程和消息拉取的过程在心里要有一个宏观的理解,说白了在心里面要清楚整个交互过程,看整个交互过程中是不是可能本身就存在这个逻辑缺陷。
2、在原来日志不能够帮助定位你的问题的时候,在可以获取源码的时候增加日志,增加在怀疑的执行路径上。
我的问题排查过程:
1、排查rocketMq订阅消息的逻辑
2、排查rocketMq订阅关系同步的逻辑
订阅过程-client端:
说明:
1、在consumer端订阅的时候我们会在本地保存一个订阅数据,在这个订阅数据里面有一个字段非常重要,就是用时间戳来代表的订阅消息版本信息。
说明:
1、定时通过心跳信息发送订阅数据到broker,也就是说我们会把订阅信息多次发送。
2、定时同步broker的订阅信息到client端,也就是最终都会拷贝到一份最新的订阅信息。
说明:
1、在我们创建SubscriptionData的时候我们其实用时间戳代表了版本号,这个东西非常重要,因为在broker端我们会通过版本号来区分最新数据。
订阅过程-broker端
说明
broker端处理的入口函数,相当于接收consumer的心跳数据的处理函数。
说明:
核心关键点,我们每次只会用最新版本号的订阅数据。
消息拉取-server端
说明
在broker端进行消费的时候我们会根据subscriptionData来判断这个消息是否属于tag内的消息,如果不是指定tag的消息,就返回false直接过滤消息。
消息拉取-client端
说明
client端也做了类似的过滤,不知道是处于什么考虑,但是broker端已经对消息进行了过滤。
结论
1、同一个consumeGroup下面的多个client定时向broker发送心跳信息,汇报自己最新的subscription信息,broker端在收到消息后以最新版本的订阅消息为准。
2、broker端在收到client拉取消息的请求后,会从broker的store中获取消息数据并以subscription信息去进行过滤,这个是关键的地方,broker在获取数据的时候会用最新的subscription去进行过滤。
3、我们这个现象原因就是旧的subscription(tag为A||D)信息和新的subscription(tag为F)信息不一致,我们以最新的subscription(tag为F)为准,这个时候即便你发送的消息tag为A||D,在消息消费的会因为最新的subscription(tag为F)被过滤掉。
其他辅助信息
在消费数据的时候会不停的打印错误日志:NO_MATCHED_MSG