概述
这篇文章是以同事在实际工作中遇到的问题作为分析的切入点,加深自己对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一致),但是事实上却没有。
![img_5713011cba26f8dfcbd706adc0887874.png](https://i-blog.csdnimg.cn/blog_migrate/d653a47c66aa27238fbaddd645afe9fb.png)
![img_6303ed1fd2f2364505d7171dada76b99.png](https://i-blog.csdnimg.cn/blog_migrate/63d9c4b9af58acfdba459fcba1558e4f.png)
![img_d7542b22176531b03a299cfae2ff78e6.png](https://i-blog.csdnimg.cn/blog_migrate/57496b512c6d997064d27ca1dcb077c2.png)
原因分析
在复现问题以后,基本上你知道离定位问题就不远了,其实对于经常出现的问题你只要静下心去排查问题就不大了,
我的问题排查理念:
1、对mq在订阅topic的过程和消息拉取的过程在心里要有一个宏观的理解,说白了在心里面要清楚整个交互过程,看整个交互过程中是不是可能本身就存在这个逻辑缺陷。
2、在原来日志不能够帮助定位你的问题的时候,在可以获取源码的时候增加日志,增加在怀疑的执行路径上。
我的问题排查过程:
1、排查rocketMq订阅消息的逻辑
2、排查rocketMq订阅关系同步的逻辑
订阅过程-client端:
![img_e04736554220bd3a09704c3c22c97115.png](https://i-blog.csdnimg.cn/blog_migrate/c05bdcf709409d57358be8281f87409c.png)
说明:
1、在consumer端订阅的时候我们会在本地保存一个订阅数据,在这个订阅数据里面有一个字段非常重要,就是用时间戳来代表的订阅消息版本信息。
![img_b27cbd7ec672df85f411ea7917fdce41.png](https://i-blog.csdnimg.cn/blog_migrate/6c5b1cd47aadc0ec8c30f64126fa7f00.png)
说明:
1、定时通过心跳信息发送订阅数据到broker,也就是说我们会把订阅信息多次发送。
2、定时同步broker的订阅信息到client端,也就是最终都会拷贝到一份最新的订阅信息。
![img_71a0ac73b72800403945c0894f067c8d.png](https://i-blog.csdnimg.cn/blog_migrate/2dc55105ff0f70f3223364880c87e147.png)
说明:
1、在我们创建SubscriptionData的时候我们其实用时间戳代表了版本号,这个东西非常重要,因为在broker端我们会通过版本号来区分最新数据。
订阅过程-broker端
![img_40d032de7abf236656c99653fb316834.png](https://i-blog.csdnimg.cn/blog_migrate/a9c7c3c8a386e8f97a3c27dcaa7ef6e8.png)
说明
broker端处理的入口函数,相当于接收consumer的心跳数据的处理函数。
![img_882b2883e58e18737fe804031b9dba22.png](https://i-blog.csdnimg.cn/blog_migrate/a2c750e907202fa98e9dd23a7836d9ed.png)
说明:
核心关键点,我们每次只会用最新版本号的订阅数据。
消息拉取-server端
![img_9c97984183da83cf2f5e6eb8b17eb30c.png](https://i-blog.csdnimg.cn/blog_migrate/e901584963999aa98e5042c00938b1ce.png)
说明
在broker端进行消费的时候我们会根据subscriptionData来判断这个消息是否属于tag内的消息,如果不是指定tag的消息,就返回false直接过滤消息。
消息拉取-client端
![img_9583d8cfa22d282801ecff76494bf478.png](https://i-blog.csdnimg.cn/blog_migrate/1e5f302c75ff12f309c9a0c7467160c1.png)
说明
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
![img_be4ded30d62709ec99ba820ccdbbdc73.png](https://i-blog.csdnimg.cn/blog_migrate/268927b0013af8aa3e0bf247a273b628.png)