3.rocketmq源代码学习----客户端消息消费(概览)

客户端消息消费

先用一张时序图来整体看下消费者启动的时候,都做了什么事情

消费者消费整体概览

在这里插入图片描述

接下来,再对上面图中的详细说明,这儿为了便于理解我们调整下顺序

1. 客户端消费者实例id
public MQClientInstance getAndCreateMQClientInstance(final ClientConfig clientConfig, RPCHook rpcHook) {
		//实例id默认为:IP@instanceName,instanceName默认为PID
        String clientId = clientConfig.buildMQClientId();
        //这儿限制了,对于同一个实例使用同一个MQClientInstance
        //所以默认情况下,同一个工程,只能连接一个mq服务器
        //当需要连接两个mq服务器时,需要设置instanceName做区分
        MQClientInstance instance = this.factoryTable.get(clientId);
        if (null == instance) {
            instance =
                    new MQClientInstance(clientConfig.cloneClientConfig(),
                            this.factoryIndexGenerator.getAndIncrement(), clientId, rpcHook);
            MQClientInstance prev = this.factoryTable.putIfAbsent(clientId, instance);
            if (prev != null) {
                instance = prev;
            } else {
                // TODO log
            }
        }

        return instance;
    }
4.startSheduledTask()启动一堆定时器

①fetchNameServerAddr:每隔2分钟,从服务器更新namesrv地址
②updateTopicRouteInfoFromNameServer:每隔30s(可配置,配置属性为:pollNameServerInteval)从namesrv服务端更新主题配置信息
③sendHeartbeatToAllBrokerWithLock:每隔30s(可配置,配置属性为:heartbeatBrokerInterval)和broker服务器保持心跳连接
④persistAllConsumerOffset:每隔5s(可配置,配置属性为:persistConsumerOffsetInterval)持久化消费进度到服务端
⑤adjustThreadPool:每隔1分钟,调整消费线程池

6.RebalanceService负载均衡线程

4.rocketmq源代码学习----客户端消息消费(负载均衡)

当新增了队列时,构造了PullRequest,并调用了computePullFromWhere()获取消费进度,这儿放到《2.RemoteOffsetStore》中去讲解

5.PullMessageService拉取消息线程

5.rocketmq源代码学习–客户端消息消费(消息拉取)

3.ConsumeMessageConcurrentlyService 消费线程

消息拉取后,提交给来ConsumeMessageConcurrentlyService 线程执行,也就是说消息队列的消息是多线程执行的… 多线?程执行的哟… 你会不会有一个疑问?那么… 那么… 消费进度怎么更新呢?多线程情况下怎么知道现在应该以哪个进度更新呢?问题我们先留在这,后面解答。
先来整体看下consumeRequest线程的run()方法:

这里写图片描述

1、当消息消费失败时,调用sendMessageBack()将消息放到另外一个主题为:%RETRY%消费者ID 的主题中,也就是说当前队列的这个消息可以理解为消费成功啦,也就是说,息消费失败,不会阻塞当前消费队列,这也解释了为什么在消费者中要启动生产者…
放入重试队列时,将根据失败次数,设置delayLevel,即rocketmq失败重试机制,最多重试16次,时间分别为:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

再来看下ProcessQueue.removeMessage()方法

public long removeMessage(final List<MessageExt> msgs) {
        long result = -1;
        final long now = System.currentTimeMillis();
        try {
            this.lockTreeMap.writeLock().lockInterruptibly();
            this.lastConsumeTimestamp = now;
            try {
                if (!msgTreeMap.isEmpty()) {
                    result = this.queueOffsetMax + 1;
                    int removedCnt = 0;
                    for (MessageExt msg : msgs) {
                        MessageExt prev = msgTreeMap.remove(msg.getQueueOffset());
                        if (prev != null) {
                            removedCnt--;
                        }
                    }
                    msgCount.addAndGet(removedCnt);

                    if (!msgTreeMap.isEmpty()) {
                        result = msgTreeMap.firstKey();
                    }
                }
            }
            finally {
                this.lockTreeMap.writeLock().unlock();
            }
        }
        catch (Throwable t) {
            log.error("removeMessage exception", t);
        }

        return result;
    }

这里面有至关重要的一句代码,msgTreeMap.firstKey();
也就是说消息消费完后,并不是以当前消息的消费进度去更新的,而是以,以最小消费进度更新,这也就解释了上面的问题,多线程消费的情况下,怎么去更新消费队列的进度…

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值