⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 yes的练级攻略 「是Yes呀」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

前不久踩了个坑,而这个坑跟 RocketMQ 推荐的一个最佳实践有关。

看下我从官网的截图,官方推荐一个应用尽可能只用一个 topic,然后用 tags 来标识子类型。

从订单角度来看,可以用一个 Topic-Order,然后再用不同的 tag 来区分这是 3C 类的订单,还是母婴类的订单等,然后下游应用根据不同的需求过滤不同的 tag。

这样的实现方式从业务上来看关系更清晰(有点树状的感觉),但是在实践上有点问题。

问题和起因

一般而言,生产上同一个服务至少会部署两台机器,不仅仅是为了负载均衡,也是为了系统的可靠性,当一台机器意外挂了,另一台可以扛起大旗。

我们在发服务的时候,都是分批发布。

这是为了验证新功能的正确性,不让其一次性影响所有实例,我们会先发布一批,然后观察下日志,确保无误后继续发布后续几台机器。

而这个操作再结合 RocketMQ 一个 Topic 多 tag 就会出现订阅消息不一致的情况,导致丢消息。

原理分析

我们借用官网的图来分析一下。

一般我们都用集群模式,以下描述默认使用集群模式

从使用层来看,发送和消费消息给我们最直观的感受如下:

生产者往一个 Topic 发送消息,消费者订阅了这个 Topic 就能消费到这个消息。

而实际上在 RocketMQ 中有队列的概念:

也就是生产者往一个 Topic 发送消息时,消息会被分到不同的队列中。

而属于同一个消费组的消费者们会平分消费这些队列,从上图可以看到 Topic A 分了三个队列,分别是 MessageQueue 0、1、2。

而消费组 ConsumerGroupA 中的 Consumer1 仅消费 MessageQueue0 和 MessageQueue1 这两个队列中的消息,而 Consumer2 仅消费 MessageQueue2。

这样划分后,Consumer1 是无法消费到 MessageQueue2 中的消息的。

看到可能有人会说,这跟 tag 有什么关系吗?没错,问题就在这个分割跟 tag 没关系!

在默认情况下生产者发送消息是以轮询队列的方式发送的。

比如现在 Producer A 要发送 TopicA-tag1、TopicA-tag2、TopicA-tag3 这三条数据,轮询发送后,MessageQueue 0、1、2 分别存储了这 3 条消息。

假设同样订阅了 TopicA,但是 Consumer 1订阅的 tag 是 tag1和 tag3,而 Consumer 2 订阅的是 tag1、tag2,那么问题就来了。

按轮询的顺序 Consumer 1 要消费的 tag3 被投递到 MessageQueue2 这个队列中,而 Consumer 1 又无法消费 MessageQueue2 中的消息,Consumer 2 能消费 MessageQueue2 中的消息,但偏偏它又不要 tag3 的消息。这样一来 tag3 的这条消息就丢了,问题就出现了。

所以,在实践中,我们要求同一个消费组的消费者的订阅关系要保持一致。

也就是 Conusmer1 和 Conusmer2 需要订阅一样的 Topic、一样的 tag,这样消息才不会丢失。

再回到问题

现在我们已经知道订阅关系一致的重要性,但是有时候不得已就会“明知故犯”。

假设我们订单服务线上一共部署了 5 台,这 5 台机器属于同一个消费组,因此它们负载均衡消费有关订单的消息,如 Topic-Order。

这 5 台机器部署的都是同一套代码,它们都订阅了 Topic-Order,且 tag 是 A、B、C 三个。

这次发版需要订单服务新增消费 Topic-Order 下的 tag D 消息,由于分批部署,所以先部署了 1 台机器观察。

而此时线上就出现了订阅关系不一致的情况!5台机器,有 1 台订阅了 Topic-Order tag A、B、C、D,而其他 4 台订阅了 Topic-Order tag A、B、C。

这段时间内就出现了上述所说的丢消息的情况,如果有 Topic-Order tagD 的消息产生,那么就有可能会丢了。

明知有错,不想犯,却犯了!

针对这个场景,我暂时没啥思路,不知道业界是否有什么方式可以优雅的处理这个问题?欢迎各位留言指导或探讨!

然后留个坑,如果一台机器订阅的是 tagA||tagB,而另一台订阅的是 tagB||tagA,这样算订阅消息一致吗?

文章目录
  1. 1. 问题和起因
  2. 2. 原理分析
  3. 3. 再回到问题