RocketMQ订阅关系一致性分析

RocketMQ的消费者订阅关系一致性至关重要,避免消息覆盖和负载均衡问题。订阅不一致可能导致消费混乱、消息丢失。本文分析了订阅覆盖和消息延迟消费的原因,详细解释了心跳处理和负载均衡过程。
摘要由CSDN通过智能技术生成

RocketMQ的消费者,在订阅topic的时候需要遵循“订阅关系一致性”原则,即:一个消费者分组(group)下的所有消费者实例的处理逻辑必须一致,一旦订阅关系不一致就会导致消费混乱,甚至消息丢失。对大多数分布式应用来说,一个group下通常会挂有多个consumer实例。由于RocketMq的消费者订阅关系由Topic+Tag组成,因此保持订阅一致就意味着,所有consumer实例需要保证:

  • 订阅的topic必须一致
  • 订阅topic中的tag必须一致

通俗的讲就是一个消费者组GroupA,有consumerA和consumerB,消费者A订阅了topicA、tagA,消费者B订阅了topicB、tagB就会导致订阅不一致问题。

不一致原因解析

问题一:订阅消息相互覆盖

消费者的信息在broker里面是通过一个map存储的,key是groupName,value是组的信息

//org.apache.rocketmq.broker.client.ConsumerManager
 private final ConcurrentMap<String/* Group */, ConsumerGroupInfo> consumerTable =
        new ConcurrentHashMap<String, ConsumerGroupInfo>(1024);

ConsumerGroupInfo里面存储了该group里面订阅的topic信息,同样使用map存储,key是topic,所以当相同group下面的消费者,订阅的topic如果不一致,就会覆盖map里面的值。

 private final ConcurrentMap<String/* Topic */, SubscriptionData> subscriptionTable =
        new ConcurrentHashMap<String, SubscriptionData>();

下面我们来看看具体的源码实现:当我们启动消费者的时候(调用DefaultMQPushConsumer.start()方法),会启动MQ客户端。

//DefaultMQPushConsumerImpl.start()
///
  mQClientFactory.start();

mq客户端会启动心跳发送线程,定时向broker发送心跳信息。

//MQClientInstance.startScheduledTask()

        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
   

            @Override
            public void run() {
   
                try {
   
                //清理离线broker
                    MQClientInstance.this.cleanOfflineBroker();
                    //发送心跳信息到broker
                    MQClientInstance.this.sendHeartbeatToAllBrokerWithLock();
                } catch (Exception e) {
   
                    log.error("ScheduledTask sendHeartbeatToAllBroker exception", e);
                }
            }
        }, 1000, this.clientConfig.getHeartbeatBrokerInterval(), TimeUnit.MILLISECONDS);

重点看这个sendHeartbeatToAllBrokerWithLock方法,点进去之后继续看sendHeartbeatToAllBroker()方法

 private void sendHeartbeatToAllBroker() {
   
        final HeartbeatData heartbeatData = this.prepareHeartbeatData();
        final boolean producerEmpty = heartbeatData.getProducerDataSet().isEmpty();
        final boolean consumerEmpty = heartbeatData.getConsumerDataSet().isEmpty();
        //如果消费者和生产者信息为空,直接返回
        if (producerEmpty && consumerEmpty) {
   
            log.warn("sending heartbeat, but no consumer and no producer");
            return;
        }

        if (!this.brokerAddrTable.isEmpty()) {
   
            //记录了发送心跳的次数
            long times = this.sendHeartbeatTimesTotal.getAndIncrement();
            Iterator<Entry<String, HashMap<Long, String>>> it = this.brokerAddrTable.entrySet().iterator();
            while (it.hasNext()) {
   
                Entry<String, HashMap<Long, String>> entry = it.next();
                String brokerName = entry.getKey();
                HashMap<Long, String> oneTable = entry.getValue();
                if (oneTable != null) {
   
                    for (Map.Entry<Long, String> entry1 : oneTable.entrySet()) {
   
                        Long id = entry1.getKey();
                        String addr = entry1.getValue();
                        if (addr != null) {
   
                            if (consumerEmpty) {
   
                                if (id != MixAll.MASTER_ID)
                                    continue;
                            }

                            try {
   
                            //向broker心跳。
                                int version = this.mQClientAPIImpl.sendHearbeat(addr, heartbeatData, 3000);
                                if (!this.brokerVersionTable.containsKey(brokerName)) {
   
                                    this
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值