rocketMq - tag不一致造成的假象

本文通过一次线上发布升级实例,分析了一个consumeGroup下因不同consumer订阅的tags信息不一致导致所有consumer无法消费数据的问题。通过复现步骤与问题排查过程揭示了RocketMQ在订阅消息与消息拉取时的机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

    这篇文章是以同事在实际工作中遇到的问题作为分析的切入点,加深自己对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一致),但是事实上却没有。

 

consumerA

 

consumerB

 

producer

 

原因分析

    在复现问题以后,基本上你知道离定位问题就不远了,其实对于经常出现的问题你只要静下心去排查问题就不大了,

我的问题排查理念:

    1、对mq在订阅topic的过程和消息拉取的过程在心里要有一个宏观的理解,说白了在心里面要清楚整个交互过程,看整个交互过程中是不是可能本身就存在这个逻辑缺陷。

    2、在原来日志不能够帮助定位你的问题的时候,在可以获取源码的时候增加日志,增加在怀疑的执行路径上。

 

我的问题排查过程:

    1、排查rocketMq订阅消息的逻辑

    2、排查rocketMq订阅关系同步的逻辑

 

订阅过程-client端:

 

消息订阅及心跳发送

说明:

    1、在consumer端订阅的时候我们会在本地保存一个订阅数据,在这个订阅数据里面有一个字段非常重要,就是用时间戳来代表的订阅消息版本信息。

说明:

    1、定时通过心跳信息发送订阅数据到broker,也就是说我们会把订阅信息多次发送。

    2、定时同步broker的订阅信息到client端,也就是最终都会拷贝到一份最新的订阅信息。

 

订阅信息数据结构

说明:

    1、在我们创建SubscriptionData的时候我们其实用时间戳代表了版本号,这个东西非常重要,因为在broker端我们会通过版本号来区分最新数据。

 

订阅过程-broker端

broker端处理过程-1

说明

    broker端处理的入口函数,相当于接收consumer的心跳数据的处理函数。

 

broker端处理过程-2

说明:

    核心关键点,我们每次只会用最新版本号的订阅数据。

 

消息拉取-server端

broker端处理数据拉取

说明

    在broker端进行消费的时候我们会根据subscriptionData来判断这个消息是否属于tag内的消息,如果不是指定tag的消息,就返回false直接过滤消息。

 

消息拉取-client端

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

 

作者:晴天哥_374
链接:https://www.jianshu.com/p/524ef06ce25a
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

`rocketmq-spring-boot-starter` 和 `rocketmq-client-java` 是 Apache RocketMQ 提供的两个同层次的客户端工具,分别适用于同的应用场景和开发框架。以下是两者的主要区别和特点: ### 1. 定位和适用场景 `rocketmq-client-java` 是 RocketMQ 的原生 Java 客户端库,提供了对 RocketMQ 所有核心功能的访问接口,包括消息的发送、消费、事务消息、延迟消息等。它适用于任何 Java 项目,尤其是那些没有使用 Spring Boot 框架的项目。开发者需要手动配置和管理 Producer、Consumer、NameServer 地址等参数,并处理底层的连接和异常[^1]。 `rocketmq-spring-boot-starter` 是基于 `rocketmq-client-java` 的封装,专为 Spring Boot 项目设计。它通过 Spring Boot 的自动配置机制简化了 RocketMQ 客户端的集成,使得开发者可以通过简单的配置文件(如 `application.yml` 或 `application.properties`)完成 RocketMQ 的初始化和使用。它更适合在 Spring Boot 项目中快速集成 RocketMQ 功能,减少了手动配置和管理的复杂性[^2]。 ### 2. 使用方式和配置 在使用 `rocketmq-client-java` 时,开发者需要手动编写代码来创建和管理生产者(`DefaultMQProducer`)和消费者(`DefaultMQPushConsumer`)实例,并在代码中指定 NameServer 地址、主题(Topic)、标签(Tag)等参数。例如: ```java DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName"); producer.setNamesrvAddr("127.0.0.1:9876"); producer.start(); ``` 而在 `rocketmq-spring-boot-starter` 中,这些配置可以通过 `application.yml` 文件完成: ```yaml rocketmq: producer: group: my-group namesrv-addr: 127.0.0.1:9876 ``` 同时,可以通过注入 `RocketMQTemplate` 来直接发送和消费消息,无需手动管理底层资源: ```java @Autowired private RocketMQTemplate rocketMQTemplate; rocketMQTemplate.convertAndSend("test-topic", "Hello RocketMQ"); ``` 这种方式极大地简化了 RocketMQ 在 Spring Boot 项目中的集成流程[^3]。 ### 3. 功能支持 `rocketmq-client-java` 提供了完整的 RocketMQ 功能支持,包括同步发送、异步发送、单向发送、拉取模式、事务消息等。开发者可以根据需求灵活地使用这些功能。 `rocketmq-spring-boot-starter` 在此基础上进行了封装,虽然也支持大部分常用功能,但某些高级特性可能需要通过自定义配置或扩展实现。例如,在某些版本中可能存在对事务消息支持完善的情况,或者需要额外的依赖配置来启用特定功能[^4]。 ### 4. 版本兼容性 由于 `rocketmq-spring-boot-starter` 是基于 `rocketmq-client-java` 的封装,因此其版本通常与 RocketMQ 核心客户端版本保持一致或存在一定的依赖关系。在使用时需要注意版本匹配问题,尤其是在 Spring Boot 3.x 等新版本框架中,可能会出现兼容性问题,需要选择合适的 starter 版本[^5]。 ### 5. 适用框架 `rocketmq-client-java` 适用于所有 Java 项目,包括传统的 Spring Framework 项目、Java EE 项目以及非 Spring 项目。 `rocketmq-spring-boot-starter` 则专为 Spring Boot 项目设计,提供了与 Spring Boot 生态系统的良好集成,如自动配置、依赖注入、健康检查等功能。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值