RocketMQ部分消息消费不到的问题--订阅关系一致

问题:

在阿里云服务提供的消息队列服务(RocketMQ)中,给出了一份关于订阅关系一致的最佳实践,在文档中指出同一个 GroupID 中的所有消费者实例最好订阅同样的 Topic+Tag。这让我有一些疑问,为什么一个 GroupID 只能产生一种订阅,这样岂不是同一个应用需要订阅不同的 Topic 那么每一次都需要去申请一个 GroupID,这使得 GroupID 和 Topic 产生了一定的耦合关系,看起来是不太合理的,那么为什么 RocketMQ 要这么做呢?
----参考:https://blog.youkuaiyun.com/A__loser/article/details/102804760

如果同一个 GroupID 下的不同消费者实例,订阅了不同的 Topic+Tag 将导致在对Topic 的消费队列进行负载均衡的时候产生不正确的结果,最终导致消息丢失。

以下摘自官网:订阅关系一致

订阅关系一致指的是同一个消费者 Group ID 下所有 Consumer 实例的处理逻辑必须完全一致。一旦订阅关系不一致,消息消费的逻辑就会混乱,甚至导致消息丢失。本文提供订阅关系一致的正确示例代码以及订阅关系不一致的错误示例代码,帮助您顺畅地订阅消息。

背景信息

消息队列 RocketMQ 版里的一个消费者 Group ID 代表一个 Consumer 实例群组。对于大多数分布式应用来说,一个消费者 Group ID 下通常会挂载多个 Consumer 实例。

由于消息队列 RocketMQ 版的订阅关系主要由 Topic + Tag 共同组成,因此,保持订阅关系一致意味着同一个消费者 Group ID 下所有的实例需在以下两方面均保持一致:

  • 订阅的 Topic 必须一致
  • 订阅的 Topic 中的 Tag 必须一致

正确订阅关系图片示例

多个 Group ID 订阅了多个 Topic,并且每个 Group ID 里的多个消费者实例的订阅关系保持了一致。

mq消息正确订阅关系

错误订阅关系图片示例

单个 Group ID 订阅了多个 Topic,但是该 Group ID 里的多个消费者实例的订阅关系并没有保持一致。

错误订阅关系

错误订阅关系代码示例一

以下例子中,同一个 Group ID 下的两个实例订阅的 Topic 不一致。

  • Consumer 实例 1-1:
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.GROUP_ID, "GID_jodie_test_1");
        Consumer consumer = ONSFactory.createConsumer(properties);
        consumer.subscribe("jodie_test_A", "*", new MessageListener() {
            public Action consume(Message message, ConsumeContext context) {
                System.out.println(message.getMsgID());
                return Action.CommitMessage;
            }
        });                    
  • Consumer 实例 1-2:
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.GROUP_ID, "GID_jodie_test_1");
        Consumer consumer = ONSFactory.createConsumer(properties);
        consumer.subscribe("jodie_test_B ", "*", new MessageListener() {
            public Action consume(Message message, ConsumeContext context) {
                System.out.println(message.getMsgID());
                return Action.CommitMessage;
            }
        });                    

错误订阅关系代码示例二

以下例子中,同一个 Group ID 下订阅 Topic 的 Tag 不一致。Consumer 实例 2-1 订阅了 TagA,而 Consumer 实例 2-2 未指定 Tag。

  • Consumer 实例 2-1:
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.GROUP_ID, "GID_jodie_test_2");
        Consumer consumer = ONSFactory.createConsumer(properties);
        consumer.subscribe("jodie_test_A", "TagA", new MessageListener() {
            public Action consume(Message message, ConsumeContext context) {
                System.out.println(message.getMsgID());
                return Action.CommitMessage;
            }
        });                    
  • Consumer 实例 2-2:
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.GROUP_ID, "GID_jodie_test_2");
        Consumer consumer = ONSFactory.createConsumer(properties);
        consumer.subscribe("jodie_test_A", "*", new MessageListener() {
            public Action consume(Message message, ConsumeContext context) {
                System.out.println(message.getMsgID());
                return Action.CommitMessage;
            }
        });                   

错误订阅关系代码示例三

此例中,同一个 Group ID 下订阅 Topic 个数不一致,且订阅的 Topic 的 Tag 不一致。

  • Consumer 实例 3-1:
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.GROUP_ID, "GID_jodie_test_3");
        Consumer consumer = ONSFactory.createConsumer(properties);
        consumer.subscribe("jodie_test_A", "TagA", new MessageListener() {
            public Action consume(Message message, ConsumeContext context) {
                System.out.println(message.getMsgID());
                return Action.CommitMessage;
            }
        });
        consumer.subscribe("jodie_test_B", "TagB", new MessageListener() {
            public Action consume(Message message, ConsumeContext context) {
                System.out.println(message.getMsgID());
                return Action.CommitMessage;
            }
        });                    
  • Consumer 实例 3-2:
    Properties properties = new Properties();
    properties.put(PropertyKeyConst.GROUP_ID, "GID_jodie_test_3");
    Consumer consumer = ONSFactory.createConsumer(properties);
    consumer.subscribe("jodie_test_A", "TagB", new MessageListener() {
        public Action consume(Message message, ConsumeContext context) {
            System.out.println(message.getMsgID());
            return Action.CommitMessage;
        }
    });    

 

史上最强Tomcat8性能优化

阿里巴巴为什么能抗住90秒100亿?--服务端高并发分布式架构演进之路

B2B电商平台--ChinaPay银联电子支付功能

学会Zookeeper分布式锁,让面试官对你刮目相看

SpringCloud电商秒杀微服务-Redisson分布式锁方案

查看更多好文,进入公众号--撩我--往期精彩

一只 有深度 有灵魂 的公众号0.0

 

RocketMQ 中的订阅关系一致问题是指在同一个消费者组(Consumer Group)中,不同的消费者实例订阅了不同的 Topic 或者相同的 Topic 但使用了不同的 Tag 配置。这种情况会导致消息消费逻辑混乱,甚至可能引发消息丢失或重复消费问题。 ### 订阅关系一致的表现 - **消息未被消费**:某些消费者可能无法接收到本应由其处理的消息- **消息重复消费**:由于订阅关系一致部分消息可能被多个消费者同时消费- **消息丢失**:在极端情况下,如果某个消费者因为订阅关系不匹配而未能正确注册到 Broker,可能会导致消息直接被跳过。 ### 解决方案 #### 1. 统一订阅关系 确保同一个 Consumer Group 下的所有消费者实例订阅Topic 和 Tag 完全一致。这是 RocketMQ 的基本要求之一。可以通过以下方式实现: - 在代码中硬编码订阅关系,确保所有消费者实例使用相同的 `subscribe` 方法配置。 - 使用配置中心管理订阅关系,避免手动修改代码带来的不一致性。 ```java DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupName"); consumer.subscribe("TopicA", "*"); // 订阅 TopicA 的所有 Tag ``` #### 2. 灰度发布时的订阅关系变更 对于需要逐步升级订阅关系的场景(如灰度发布),建议采用以下策略: - **旧版本并行运行**:在同一时间段内,保留旧版本的消费者实例与版本的消费者实例共同运行,确保不会因订阅关系变更导致消息丢失。 - **逐步切换订阅关系**:通过分阶段的方式逐步将版本消费者引入到系统中,每个阶段都进行充分的测试和监控,确保订阅关系变更不会影响现有业务。 #### 3. 消费失败排查 当发现有消息未被正常消费时,可以按照以下步骤进行排查: - **确认消息是否发送成功**:使用消息 ID、Key 或发送时间范围在 RocketMQ Console 中查找消息,验证消息是否确实已发送至 Broker [^2]。 - **检查订阅关系是否一致**:确保消费者的订阅 Topic 和 Tag 与生产者的发送 Topic 和 Tag 完全一致 [^3]。 - **查看消费日志**:检查消费者是否抛出异常或未记录日志,导致消息看似“消失”。 - **网络连通性检查**:确认消费者与 Namesrv 或 Broker 之间的网络连接是否正常 [^2]。 #### 4. 生产环境优化建议 - **避免频繁变更订阅关系**:尽量减少对已有 Consumer Group 的订阅关系修改,尤其是在高并发、大数据量的生产环境中。 - **使用独立 Consumer Group 进行升级**:对于需要变更订阅关系的情况,建议创建的 Consumer Group,并在其上部署版本的服务,待验证无误后再替换旧 Group。 - **启用消息回放功能**:对于关键业务,可以预先配置好消息回放机制,以便在出现问题时能够快速恢复数据。 --- ###
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值