RocketMQ:Consumer概述及启动流程与消息拉取源码分析

Consumer

概述

  • 消费者组与消费模式

    消息消费以组的模式展开,一个消费组内可包含多个消费者,每个消费组可以订阅多个主题。消费组之间有负载均衡和广播两种模式。负载均衡模式,主题下的同一条消息只允许被其中一个消费者消费。广播模式,主题下的同一条消息,将被所有消费者消费一次。

  • 消息传递模式

    分为推送和拉取两种模式。

  • 从何处开始消费消息,可选参数:

    CONSUME_FROM_LAST_OFFSET:上一次消费偏移量

    CONSUME_FROM_FIRST_OFFSET:从头开始

    CONSUME_FROM_TIMESTAMP:某个时间开始

消费者核心类

基于消息推送模式

在这里插入图片描述

//发送消息-如果消息消费失败,将会发送回Broker,过一段时间(delayLevel)再进行消费
void sendMessageBack(final MessageExt msg, final int delayLevel, final String brokerName);
//根据主题从消费者缓存中获取消息队列
Set<MessageQueue> fetchSubscribeMessageQueues(final String topic);
//注册并发消息事件监听器
void registerMessageListener(MessageListenerConcurrently messageListener);
//注册顺序消息事件监听器
void registerMessageListener(final MessageListenerOrderly messageListener);
//基于主题订阅消息,消息过滤使用表达式
void subscribe(final String topic, final String subExpression);
//基于主题订阅消息,消息过滤使用类模式
void subscribe(final String topic, final String fullClassName,final String filterClassSource);
//订阅消息,并指定队列选择器
void subscribe(final String topic, final MessageSelector selector);
void unsubscribe(final String topic):取消消息订阅

DefaultMQPushConsumer

在这里插入图片描述

//消费者组
private String consumerGroup;	
//消息消费模式
private MessageModel messageModel = MessageModel.CLUSTERING;	
//指定消费开始偏移量(最大偏移量、最小偏移量、启动时间戳)开始消费
private ConsumeFromWhere consumeFromWhere = ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET;
//集群模式下的消息队列负载策略
private AllocateMessageQueueStrategy allocateMessageQueueStrategy;
//订阅信息
private Map<String /* topic */, String /* sub expression */> subscription = new HashMap<String, String>();
//消息业务监听器
private MessageListener messageListener;
//消息消费进度存储器
private OffsetStore offsetStore;
//消费者最小线程数量
private int consumeThreadMin = 20;
//消费者最大线程数量
private int consumeThreadMax = 20;
//并发消息消费时处理队列最大跨度
private int consumeConcurrentlyMaxSpan = 2000;
//每1000次流控后打印流控日志
private int pullThresholdForQueue = 1000;
//推模式下任务间隔时间
private long pullInterval = 0;
//推模式下任务拉取的条数,默认32条
private int pullBatchSize = 32;
//每次传入MessageListener#consumerMessage中消息的数量
private int consumeMessageBatchMaxSize = 1;
//是否每次拉取消息都订阅消息
private boolean postSubscriptionWhenPull = false;
//消息重试次数,-1代表16次
private int maxReconsumeTimes = -1;
//消息消费超时时间
private long consumeTimeout = 15;

DefaultMQPushConsumerImpl:消息消费者默认实现类,应用程序中直接调用该类的实例完成消息的消费。

RebalanceImpl:重新负载均衡实现类,实现消息消费端与消息队列之间的重新分布。

MQClientInstance:消息客户端实例,负责与MQ服务器BrokerNameServer之间的的网络交互。

PullAPIWrapper:RocketMQ中,实际上只有Message Pull模式,而Push模式只是将Pull模式进行了封装。

OffsetStore:消息消费进度存储器。

消费者启动流程

在这里插入图片描述

DefaultMQPushConsumerImpl#start

switch (this.serviceState) {
    case CREATE_JUST:
        log.info("the consumer [{}] start beginning. messageModel={}, isUnitMode={}", this.defaultMQPushConsumer.getConsumerGroup(),
            this.defaultMQPushConsumer.getMessageModel(), this.defaultMQPushConsumer.isUnitMode());
        this.serviceState = ServiceState.START_FAILED;

        //校验消息合法性
        this.checkConfig();

        //构建主题订阅信息
        this.copySubscription();

        /**
         * 如果消息消费模式为集群模式,并且当前的实例名为 DEFAULT,替换为当前客户端进程的PID
         *   if (this.instanceName.equals("DEFAULT")) {
         *      this.instanceName = UtilAll.getPid() + "#" + System.nanoTime();
         *   }
         */
        if (this.defaultMQPushConsumer.getMessageModel() == MessageModel.CLUSTERING) {
            this.defaultMQPushConsumer.changeInstanceNameToPID();
        }


        //构建MQClientInstance mQClientFactory
        this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQPushConsumer, this.rpcHook);

        //构造重新负载均衡实现类
        this.rebalanceImpl.setConsumerGroup(this.defaultMQPushConsumer.getConsumerGroup());
        this.rebalanceImpl.setMessageModel(this.defaultMQPushConsumer.getMessageModel());
        this.rebalanceImpl.setAllocateMessageQueueStrategy(this.defaultMQPushConsumer.getAllocateMessageQueueStrategy());
        this.rebalanceImpl.setmQClientFactory(this.mQClientFactory);

        //拉取消息API封装
        this.pullAPIWrapper = new PullAPIWrapper(
            mQClientFactory,
            this.defaultMQPushConsumer.getConsumerGroup(), isUnitMode());
        this.pullAPIWrapper.registerFilterMessageHook(filterMessageHookList);

        //消息消费进度加载
        if (this.defaultMQPushConsumer.getOffsetStore() != null) {
            this.offsetStore = this.defaultMQPushConsumer.getOffsetStore();
        } else {
            switch (this.defaultMQPushConsumer.getMessageModel()) {
                case BROADCASTING:
                    //广播模式下 将消息消费进度存储到本地
                    this.offsetStore = new LocalFileOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
                    break;
                case CLUSTERING:
                    //集群模式下 将消息消费的进度存储到远端Broker中
                    this.offsetStore = new RemoteBrokerOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
                    break;
                default:
                    break;
            }
            this.defaultMQPushConsumer.setOffsetStore(this.offsetStore);
        }
        this.offsetStore.load();

        //创建顺序消息消费服务
        if (this.getMessageListenerInner() instanceof MessageListenerOrderly) {
            //顺序
            this.consumeOrderly = true;
            this.consumeMessageService =
                new ConsumeMessageOrderlyService(this, (MessageListenerOrderly) this.getMessageListenerInner());
        }
        //创建并发消息消费服务
        else if (this.getMessageListenerInner() instanceof MessageListenerConcurrently) {
            //并发
            this.consumeOrderly = false;
            this.consumeMessageService =
                new ConsumeMessageConcurrentlyService(this, (MessageListenerConcurrently) this.getMessageListenerInner());
        }

        //启动消息消费服务
        this.consumeMessageService.start();

        //注册到消费者实例到客户端
        boolean registerOK = mQClientFactory.registerConsumer(this.defaultMQPushConsumer.getConsumerGroup(), this);
        if (!registerOK) {
            this.serviceState = ServiceState.CREATE_JUST;
            this.consumeMessageService.shutdown(defaultMQPushConsumer.getAwaitTerminationMillisWhenShutdown());
            throw new MQClientException("The consumer group[" + this.defaultMQPushConsumer.getConsumerGroup()
                + "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL),
                null);
        }

        //--------启动客户端---------
        mQClientFactory.start();
        log.info("the consumer [{}] start OK.", this.defaultMQPushConsumer.getConsumerGroup());
        //进入消息消费状态
        this.serviceState = ServiceState.RUNNING;
        break;
    case RUNNING:
    case START_FAILED:
    case SHUTDOWN_ALREADY:
        throw new MQClientException("The PushConsumer service state not OK, maybe started once, "
            + this.serviceState
            + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),
            null);
    default:
        break;
}

//更新订阅信息
this.updateTopicSubscribeInfoWhenSubscriptionChanged();
//检测broker状态
this.mQClientFactory.checkClientInBroker();
//发送心跳包
this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
//重新负载
this.mQClientFactory.rebalanceImmediately();

消息拉取

核心类:PutMessageService,是一个消息拉取的服务线程。

PullMessageService实现机制

在这里插入图片描述

PullMessageService#run

//线程状态为运行状态
while (!this.isStopped()) {
    try {
        //在拉取消息请求队列中拉取消息请求
        PullRequest pullRequest = this.pullRequestQueue.take();
        //处理拉取消息请求
        this.pullMessage(pullRequest);
    } catch (InterruptedException ignored) {
    } catch (Exception e) {
        log.error("Pull Message Service Run Method exception", e);
    }
}

PullRequest

public class PullRequest {
	//费者组
    private String consumerGroup;
	//待拉取消息队列
    private MessageQueue messageQueue;
	//消息处理队列
    private ProcessQueue processQueue;
	//待拉取的MessageQueue偏移量
    private long nextOffset;
	//是否被锁定
    private boolean previouslyLocked = false;
}    

PullMessageService#pullMessage

//找到消费者
final MQConsumerInner consumer = this.mQClientFactory.selectConsumer(pullRequest.getConsumerGroup());
if (consumer != null) {
    //强转为推送模式下的消费者
    DefaultMQPushConsumerImpl impl = (DefaultMQPushConsumerImpl) consumer;
    //拉取消息
    impl.pullMessage(pullRequest);
} else {
    log.warn("No matched consumer for the PullRequest {}, drop it", pullRequest);
}

ProcessQueue实现机制

ProcessQueueMessageQueue在消费端的快照。PullMessageService从消息服务器默认每次拉取32条消息,按照消息的队列偏移量顺序放在ProcessQueue中,PullMessageService再将消息提交到消费者消费线程池,消息消费成功之后从ProcessQueue中移除——Queue consumption snapshot

ProcessQueue

//消息容器
private final TreeMap<Long, MessageExt> msgTreeMap = new TreeMap<Long, MessageExt>();
//读写锁
private final ReadWriteLock lockTreeMap = new ReentrantReadWriteLock();
//ProcessQueue总消息数量
private final AtomicLong msgCount = new AtomicLong();
//ProcessQueue队列最大偏移量
private volatile long queueOffsetMax = 0L;
//当前ProcessQueue是否被丢弃
private volatile boolean dropped = false;
//上一次拉取时间戳
private volatile long lastPullTimestamp = System.currentTimeMillis();
//上一次消费时间戳
private volatile long lastConsumeTimestamp = System.currentTimeMillis();
//移除消费超时消息
public void cleanExpiredMsg(DefaultMQPushConsumer pushConsumer)
//添加消息
public boolean putMessage(final List<MessageExt> msgs)
//获取消息最大间隔
public long getMaxSpan()
//移除消息
public long removeMessage(final List<MessageExt> msgs)
//将consumingMsgOrderlyTreeMap中消息重新放在msgTreeMap,并清空consumingMsgOrderlyTreeMap   
public void rollback() 
//将consumingMsgOrderlyTreeMap消息清除,表示成功处理该批消息
public long commit()
//重新处理该批消息
public void makeMessageToCosumeAgain(List<MessageExt> msgs) 
//从processQueue中取出batchSize条消息
public List<MessageExt> takeMessags(final int batchSize)

消息拉取基本流程
客户端发起消息拉取请求

在这里插入图片描述

DefaultMessagePushConsumerImpl#pullMessage

//获取消息处理队列
final ProcessQueue processQueue = pullRequest.getProcessQueue();
if (processQueue.isDropped()) {
    log.info("the pull request[{}] is dropped.", pullRequest.toString());
    return;
}

//设置拉取时间戳
pullRequest.getProcessQueue().setLastPullTimestamp(System.currentTimeMillis());

try {
    //判断服务是否状态正常
    this.makeSureStateOK();
} catch (MQClientException e) {
    log.warn("pullMessage exception, consumer state not ok", e);
    this.executePullRequestLater(pullRequest, pullTimeDelayMillsWhenException);
    return;
}

//被挂起--->等待1s
if (this.isPause()) {
    log.warn("consumer was paused, execute pull request later. instanceName={}, group={}", this.defaultMQPushConsumer.getInstanceName(), this.defaultMQPushConsumer.getConsumerGroup());
    this.executePullRequestLater(pullRequest, PULL_TIME_DELAY_MILLS_WHEN_SUSPEND);
    return;
}

//-----------------------------------流量控制-----------------------------------
//获得最大待处理消息数量
long cachedMessageCount = processQueue.getMsgCount().get();
//获得最大待处理消息大小
long cachedMessageSizeInMiB = processQueue.getMsgSize().get() / (1024 * 1024);

//从数量进行流控->消息数量大于1000条
if (cachedMessageCount > this.defaultMQPushConsumer.getPullThresholdForQueue()) {
    //延迟消息拉取50ms
    this.executePullRequestLater(pullRequest, PULL_TIME_DELAY_MILLS_WHEN_FLOW_CONTROL);
    if ((queueFlowControlTimes++ % 1000) == 0) {
        log.warn(
            "the cached message count exceeds the threshold {}, so do flow control, minOffset={}, maxOffset={}, count={}, size={} MiB, pullRequest={}, flowControlTimes={}",
            this.defaultMQPushConsumer.getPullThresholdForQueue(), processQueue.getMsgTreeMap().firstKey(), processQueue.getMsgTreeMap().lastKey(), cachedMessageCount, cachedMessageSizeInMiB, pullRequest, queueFlowControlTimes);
    }
    return;
}

//从消息大小进行流控->消息大小大于100Mb
if (cachedMessageSizeInMiB > this.defaultMQPushConsumer.getPullThresholdSizeForQueue()) {
    //延迟消息拉取50ms
    this.executePullRequestLater(pullRequest, PULL_TIME_DELAY_MILLS_WHEN_FLOW_CONTROL);
    if ((queueFlowControlTimes++ % 1000) == 0) {
        log.warn(
            "the cached message size exceeds the threshold {} MiB, so do flow control, minOffset={}, maxOffset={}, count={}, size={} MiB, pullRequest={}, flowControlTimes={}",
            this.defaultMQPushConsumer.getPullThresholdSizeForQueue(), processQueue.getMsgTreeMap().firstKey(), processQueue.getMsgTreeMap().lastKey(), cachedMessageCount, cachedMessageSizeInMiB, pullRequest, queueFlowControlTimes);
    }
    return;
}

....

//获得主题订阅信息
final SubscriptionData subscriptionData = this.rebalanceImpl.getSubscriptionInner().get(pullRequest.getMessageQueue().getTopic());
//如果主题订阅信息为空--->延迟3s后继续拉取
if (null == subscriptionData) {
    this.executePullRequestLater(pullRequest, pullTimeDelayMillsWhenException);
    log.warn("find the consumer's subscription failed, {}", pullRequest);
    return;
}

final long beginTimestamp = System.currentTimeMillis();

//拉取消息回调函数
PullCallback pullCallback = new PullCallback() {
   //这一部分实现在之后进行讲解
};

....

//获取消息系统拉取标记
int sysFlag = PullSysFlag.buildSysFlag(
    commitOffsetEnable, // commitOffset
    true, // suspend
    subExpression != null, // subscription
    classFilter // class filter
);

try {
    //与服务端进行交互获取消息
    this.pullAPIWrapper.pullKernelImpl(
        pullRequest.getMessageQueue(),
        subExpression,
        subscriptionData.getExpressionType(),
        subscriptionData.getSubVersion(),
        pullRequest.getNextOffset(),
        this.defaultMQPushConsumer.getPullBatchSize(),
        sysFlag,
        commitOffsetValue,
        BROKER_SUSPEND_MAX_TIME_MILLIS,
        CONSUMER_TIMEOUT_MILLIS_WHEN_SUSPEND,
        CommunicationMode.ASYNC,
        //-----------↓↓↓注意这个回调函数-------------
        pullCallback
    );
} catch (Exception e) {
    log.error("pullKernelImpl exception", e);
    this.executePullRequestLater(pullRequest, pullTimeDelayMillsWhenException);
}

接下来重点关注pullAPIWrapper.pullKernelImpl()的核心逻辑:

public PullResult pullKernelImpl(
    final MessageQueue mq,			//消息消费队列
    final String subExpression,		//消息订阅子模式subscribe( topicName, "模式")
    final String expressionType,	
    final long subVersion,		    //版本
    final long offset,			    //pullRequest.getNextOffset()
    final int maxNums,				//defaultMQPushConsumer.getPullBatchSize()->32条
    final int sysFlag,				//系统标记
    final long commitOffset,		//当前消息队列最新偏移量
    final long brokerSuspendMaxTimeMillis,//运行Broker挂起最长时间->15s
    final long timeoutMillis,		//超时时间->30s
    final CommunicationMode communicationMode,//Sync/Async/Oneway
    final PullCallback pullCallback		//消息拉取后的回调函数
) throws MQClientException, RemotingException, MQBrokerException, InterruptedException 

PullAPIWrapper#pullKernelImpl

//获取Broker信息
FindBrokerResult findBrokerResult =
    this.mQClientFactory.findBrokerAddressInSubscribe(mq.getBrokerName(),
        this.recalculatePullFromWhichNode(mq), false);
//如果Broker信息为空
if (null == findBrokerResult) {
    //从Nameserver更新主题路由表信息
    this.mQClientFactory.updateTopicRouteInfoFromNameServer(mq.getTopic());
    //重新获取Broker信息
    findBrokerResult =
        this.mQClientFactory.findBrokerAddressInSubscribe(mq.getBrokerName(),
            this.recalculatePullFromWhichNode(mq), false);
}

if (findBrokerResult != null) {
    {
        //检查版本
        if (!ExpressionType.isTagType(expressionType)
            && findBrokerResult.getBrokerVersion() < MQVersion.Version.V4_1_0_SNAPSHOT.ordinal()) {
            throw new MQClientException("The broker[" + mq.getBrokerName() + ", "
                + findBrokerResult.getBrokerVersion() + "] does not upgrade to support for filter message by " + expressionType, null);
        }
    }
    int sysFlagInner = sysFlag;

    if (findBrokerResult.isSlave()) {
        sysFlagInner = PullSysFlag.clearCommitOffsetFlag(sysFlagInner);
    }

    //封装拉取消息请求
    PullMessageRequestHeader requestHeader = new PullMessageRequestHeader();
    requestHeader.setConsumerGroup(this.consumerGroup);
    requestHeader.setTopic(mq.getTopic());
    requestHeader.setQueueId(mq.getQueueId());
    requestHeader.setQueueOffset(offset);
    requestHeader.setMaxMsgNums(maxNums);
    requestHeader.setSysFlag(sysFlagInner);
    requestHeader.setCommitOffset(commitOffset);
    requestHeader.setSuspendTimeoutMillis(brokerSuspendMaxTimeMillis);
    requestHeader.setSubscription(subExpression);
    requestHeader.setSubVersion(subVersion);
    requestHeader.setExpressionType(expressionType);

    String brokerAddr = findBrokerResult.getBrokerAddr();
    if (PullSysFlag.hasClassFilterFlag(sysFlagInner)) {
        brokerAddr = computePullFromWhichFilterServer(mq.getTopic(), brokerAddr);
    }

    //根据brokerAddr、requestHeader等信息利用远程网络调用实例在Broker中对消息进行拉取
    //最后返回一个消息拉取结果
    PullResult pullResult = this.mQClientFactory.getMQClientAPIImpl().pullMessage(
        brokerAddr,
        requestHeader,
        timeoutMillis,
        communicationMode,
        pullCallback);

    return pullResult;
}

根据拉取消息的模式是异步或同步来进行不同操作。

MQClientAPIImpl#pullMessage

RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.PULL_MESSAGE, requestHeader);

switch (communicationMode) {
    case ONEWAY:
        assert false;
        return null;
    case ASYNC:
        //异步拉取->发送拉取消息请求并立即返回结果
        this.pullMessageAsync(addr, request, timeoutMillis, pullCallback);
        return null;
    case SYNC:
        //同步拉取->发送消息拉取请求并等待结果返回
        return this.pullMessageSync(addr, request, timeoutMillis);
    default:
        assert false;
        break;
}
return null;

消息服务器Broker组装消息

消息服务器接收到消费者的消息拉取请求之后进行消息组装的时序图:

在这里插入图片描述

消息请求处理器:PullMessageProcessor

public class PullMessageProcessor extends AsyncNettyRequestProcessor implements NettyRequestProcessor {
    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
    //Broker控制器
    private final BrokerController brokerController;	
    //构造函数存储容器
    private List<ConsumeMessageHook> consumeMessageHookList;
}    

PullMessageProcessor#processRequest

//构建消息过滤器
MessageFilter messageFilter;
if (this.brokerController.getBrokerConfig().isFilterSupportRetry()) {
    messageFilter = new ExpressionForRetryMessageFilter(subscriptionData, consumerFilterData,
        this.brokerController.getConsumerFilterManager());
} else {
    messageFilter = new ExpressionMessageFilter(subscriptionData, consumerFilterData,
        this.brokerController.getConsumerFilterManager());
}

//调用MessageStore.getMessage查找消息
final GetMessageResult getMessageResult =
    this.brokerController.getMessageStore().getMessage(
            requestHeader.getConsumerGroup(),//消费组名称
            requestHeader.getTopic(),//主题名称
            requestHeader.getQueueId(),//队列ID
            requestHeader.getQueueOffset(),//待拉取偏移量
            requestHeader.getMaxMsgNums(), //最大拉取消息条数
            messageFilter //消息过滤器
    );

DefaultMessageStore#getMessage

GetMessageStatus status = GetMessageStatus.NO_MESSAGE_IN_QUEUE;
//查找下一次队列偏移量
long nextBeginOffset = offset;
//当前消息队列最小偏移量
long minOffset = 0;
//当前消息队列最大偏移量
long maxOffset = 0;

//懒加载-当找到消息再进行赋值
GetMessageResult getResult = null;

//当前commitLog最大偏移量
final long maxOffsetPy = this.commitLog.getMaxOffset();

//根据主题名称和队列编号获取消息消费队列
ConsumeQueue consumeQueue = findConsumeQueue(topic, queueId);
if (consumeQueue != null) {
    minOffset = consumeQueue.getMinOffsetInQueue();
    maxOffset = consumeQueue.getMaxOffsetInQueue();

    //消息偏移量异常情况校对下一次拉取偏移量
    if (maxOffset == 0) {
        //表示当前消息队列中没有消息
        status = GetMessageStatus.NO_MESSAGE_IN_QUEUE;
        nextBeginOffset = nextOffsetCorrection(offset, 0);
    } else if (offset < minOffset) {
        //待拉取消息的偏移量小于队列的实际偏移量
        status = GetMessageStatus.OFFSET_TOO_SMALL;
        nextBeginOffset = nextOffsetCorrection(offset, minOffset);
    } else if (offset == maxOffset) {
        //待拉取偏移量为队列最大偏移量
        status = GetMessageStatus.OFFSET_OVERFLOW_ONE;
        nextBeginOffset = nextOffsetCorrection(offset, offset);
    } else if (offset > maxOffset) {
        //偏移量越界
        status = GetMessageStatus.OFFSET_OVERFLOW_BADLY;
        if (0 == minOffset) {
            nextBeginOffset = nextOffsetCorrection(offset, minOffset);
        } else {
            nextBeginOffset = nextOffsetCorrection(offset, maxOffset);
        }
    } else {
        //偏移量处于正常情况
         //-------------根据偏移量从CommitLog中拉取32条消息------------
        SelectMappedBufferResult selectResult = this.commitLog.getMessage(offsetPy, sizePy);

    }

PullMessageProcessor#processRequest

//根据拉取结果填充responseHeader
response.setRemark(getMessageResult.getStatus().name());
responseHeader.setNextBeginOffset(getMessageResult.getNextBeginOffset());
responseHeader.setMinOffset(getMessageResult.getMinOffset());
responseHeader.setMaxOffset(getMessageResult.getMaxOffset());

if (getMessageResult.isSuggestPullingFromSlave()) {
    //如果当前拉取消息是从Slave节点拉取并且拉取速度较慢
    responseHeader.setSuggestWhichBrokerId(subscriptionGroupConfig.getWhichBrokerWhenConsumeSlowly());
} else {
    //设置下一次拉取任务的ID为主节点
    responseHeader.setSuggestWhichBrokerId(MixAll.MASTER_ID);
}

switch (this.brokerController.getMessageStoreConfig().getBrokerRole()) {
    case ASYNC_MASTER:
    case SYNC_MASTER:
        break;
    case SLAVE:
        if (!this.brokerController.getBrokerConfig().isSlaveReadEnable()) {
            response.setCode(ResponseCode.PULL_RETRY_IMMEDIATELY);
            responseHeader.setSuggestWhichBrokerId(MixAll.MASTER_ID);
        }
        break;
}
//GetMessageResult与Response的Code转换
switch (getMessageResult.getStatus()) {
    //成功
    case FOUND:
        response.setCode(ResponseCode.SUCCESS);
        break;
    //消息不存在
    case MESSAGE_WAS_REMOVING:
        //消息重试
        response.setCode(ResponseCode.PULL_RETRY_IMMEDIATELY);
        break;
    //没有找到匹配的主题队列
    case NO_MATCHED_LOGIC_QUEUE:
    //消息队列中未包含消息
    case NO_MESSAGE_IN_QUEUE:
        if (0 != requestHeader.getQueueOffset()) {
            response.setCode(ResponseCode.PULL_OFFSET_MOVED);

            // XXX: warn and notify me
            log.info("the broker store no queue data, fix the request offset {} to {}, Topic: {} QueueId: {} Consumer Group: {}",
                requestHeader.getQueueOffset(),
                getMessageResult.getNextBeginOffset(),
                requestHeader.getTopic(),
                requestHeader.getQueueId(),
                requestHeader.getConsumerGroup()
            );
        } else {
            response.setCode(ResponseCode.PULL_NOT_FOUND);
        }
        break;
    //未找到匹配的消息
    case NO_MATCHED_MESSAGE:
        response.setCode(ResponseCode.PULL_RETRY_IMMEDIATELY);
        break;
    //消息物理偏移量为空
    case OFFSET_FOUND_NULL:
        response.setCode(ResponseCode.PULL_NOT_FOUND);
        break;
    //offset越界
    case OFFSET_OVERFLOW_BADLY:
        response.setCode(ResponseCode.PULL_OFFSET_MOVED);
        // XXX: warn and notify me
        log.info("the request offset: {} over flow badly, broker max offset: {}, consumer: {}",
            requestHeader.getQueueOffset(), getMessageResult.getMaxOffset(), channel.remoteAddress());
        break;
    //offset过大
    case OFFSET_OVERFLOW_ONE:
        response.setCode(ResponseCode.PULL_NOT_FOUND);
        break;
    //offset过小
    case OFFSET_TOO_SMALL:
        response.setCode(ResponseCode.PULL_OFFSET_MOVED);
        log.info("the request offset too small. group={}, topic={}, requestOffset={}, brokerMinOffset={}, clientIp={}",
            requestHeader.getConsumerGroup(), requestHeader.getTopic(), requestHeader.getQueueOffset(),
            getMessageResult.getMinOffset(), channel.remoteAddress());
        break;
    default:
        assert false;
        break;
}
....
//如果CommitLog标记可用,并且当前Broker为主节点,则更新消息消费进度
boolean storeOffsetEnable = brokerAllowSuspend;
storeOffsetEnable = storeOffsetEnable && hasCommitOffsetFlag;
storeOffsetEnable = storeOffsetEnable && this.brokerController.getMessageStoreConfig().getBrokerRole() != BrokerRole.SLAVE;
if (storeOffsetEnable) {
    this.brokerController.getConsumerOffsetManager().commitOffset(RemotingHelper.parseChannelRemoteAddr(channel),
													requestHeader.getConsumerGroup(),
                                                         requestHeader.getTopic(), 
													requestHeader.getQueueId(),
                                                         requestHeader.getCommitOffset());
}
return response;

消息拉取客户端处理消息

在这里插入图片描述

MQClientAPIImpl#processPullResponse

PullStatus pullStatus = PullStatus.NO_NEW_MSG;
//判断响应结果
switch (response.getCode()) {
    case ResponseCode.SUCCESS:
        pullStatus = PullStatus.FOUND;
        break;
    case ResponseCode.PULL_NOT_FOUND:
        pullStatus = PullStatus.NO_NEW_MSG;
        break;
    case ResponseCode.PULL_RETRY_IMMEDIATELY:
        pullStatus = PullStatus.NO_MATCHED_MSG;
        break;
    case ResponseCode.PULL_OFFSET_MOVED:
        pullStatus = PullStatus.OFFSET_ILLEGAL;
        break;

    default:
        throw new MQBrokerException(response.getCode(), response.getRemark(), addr);
}

//解码响应头
PullMessageResponseHeader responseHeader =
    (PullMessageResponseHeader) response.decodeCommandCustomHeader(PullMessageResponseHeader.class);

//封装PullResultExt返回
return new PullResultExt(pullStatus, responseHeader.getNextBeginOffset(), responseHeader.getMinOffset(),
    responseHeader.getMaxOffset(), null, responseHeader.getSuggestWhichBrokerId(), response.getBody());

PullResult

/**
 * 拉取结果状态:FOUND/NO_NEW_MSG/NO_MATCHED_MSG/OFFSET_ILLEGAL
 */
private final PullStatus pullStatus;
/**
 * 下次拉取偏移量
 */
private final long nextBeginOffset;
/**
 *  消息队列最小偏移量
 */
private final long minOffset;
/**
 * 消息队列最大偏移量
 */
private final long maxOffset;
/**
 * 拉取的消息队列
 */
private List<MessageExt> msgFoundList;

DefaultMQPushConsumerImpl#pullMessage

PullCallback pullCallback = new PullCallback() {
    @Override
    public void onSuccess(PullResult pullResult) {

        switch (pullResult.getPullStatus()) {
            case FOUND:
                long prevRequestOffset = pullRequest.getNextOffset();
                pullRequest.setNextOffset(pullResult.getNextBeginOffset());
                long pullRT = System.currentTimeMillis() - beginTimestamp;
                DefaultMQPushConsumerImpl.this.getConsumerStatsManager().incPullRT(pullRequest.getConsumerGroup(),
                                                                                   pullRequest.getMessageQueue().getTopic(), pullRT);

                long firstMsgOffset = Long.MAX_VALUE;
                if (pullResult.getMsgFoundList() == null || pullResult.getMsgFoundList().isEmpty()) {
                    DefaultMQPushConsumerImpl.this.executePullRequestImmediately(pullRequest);
                } else {
                    firstMsgOffset = pullResult.getMsgFoundList().get(0).getQueueOffset();

                    DefaultMQPushConsumerImpl.this.getConsumerStatsManager().incPullTPS(pullRequest.getConsumerGroup(),
                                                                                        pullRequest.getMessageQueue().getTopic(), pullResult.getMsgFoundList().size());

                    //消息拉取成功-将消息放入processQueue
                    boolean dispatchToConsume = processQueue.putMessage(pullResult.getMsgFoundList());
                    //提交消息消费请求-对消息进行处理
                    DefaultMQPushConsumerImpl.this.consumeMessageService.submitConsumeRequest(
                        pullResult.getMsgFoundList(),
                        processQueue,
                        pullRequest.getMessageQueue(),
                        dispatchToConsume);

                    //如果pullInterval大于0,则等待pullInterval毫秒后将pullRequest对象放入到PullMessageService中的pullRequestQueue队列中
                    if (DefaultMQPushConsumerImpl.this.defaultMQPushConsumer.getPullInterval() > 0) {
                        DefaultMQPushConsumerImpl.this.executePullRequestLater(pullRequest,
 								DefaultMQPushConsumerImpl.this.defaultMQPushConsumer.getPullInterval());
                    } else {
                        DefaultMQPushConsumerImpl.this.executePullRequestImmediately(pullRequest);
                    }
                }
                ....

总结

在这里插入图片描述

本文仅作为个人学习使用,如有不足或错误请指正!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

又蠢又笨的懒羊羊程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值