消费流程
拉取消息:PullMessageService
MQClientInstance#start方法,会启动消息拉取服务:PullMessageService,PullMessageService是ServiceThread的子类,启动该服务时会创建一个新的线程
public void run() { log.info(this.getServiceName() + " service started"); 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); } } log.info(this.getServiceName() + " service end"); }
在PullMessageService#run()方法中,该方法会从pullRequestQueue中获取一个pullRequest的操作,然后调用this.pullMessage(pullRequest)进行拉取,注意pullRequest类型为LinkedBlockingQueue,使用的是阻塞方法take()。this.pullMessage(pullRequest)方法对pullRequest做进一步处理
private void pullMessage(final PullRequest pullRequest) { 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); } }
调用的是DefaultMQPushConsumerImpl#pullMessage来进一步处理pullRequest,最核心就是拉取消息的操作了,方法为PullAPIWrapper#pullKernelImpl
public PullResult pullKernelImpl(final MessageQueue mq, final String subExpression,
final String expressionType, final long subVersion, final long offset, final int maxNums, final int sysFlag, final long commitOffset, final long brokerSuspendMaxTimeMillis,final long timeoutMillis,final CommunicationMode communicationMode,final PullCallback pullCallback ) throws MQClientException, RemotingException, MQBrokerException, InterruptedException { FindBrokerResult findBrokerResult = this.mQClientFactory.findBrokerAddressInSubscribe(mq.getBrokerName(), this.recalculatePullFromWhichNode(mq), false); if (null == findBrokerResult) { this.mQClientFactory.updateTopicRouteInfoFromNameServer(mq.getTopic()); findBrokerResult = this.mQClientFactory.findBrokerAddressInSubscribe(mq.getBrokerName(), this.recalculatePullFromWhichNode(mq), false); } if (findBrokerResult != null) { { // check version 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); } PullResult pullResult = this.mQClientFactory.getMQClientAPIImpl().pullMessage( brokerAddr, requestHeader, timeoutMillis, communicationMode, pullCallback); return pullResult; } throw new MQClientException("The broker[" + mq.getBrokerName() + "] not exist", null); }
方法主要是组装拉取消息的请求,组装好之后接着就调用了MQClientAPIImpl#pullMessage方法
public PullResult pullMessage( final String addr, final PullMessageRequestHeader requestHeader, final long timeoutMillis, final CommunicationMode communicationMode, final PullCallback pullCallback ) throws RemotingException, MQBrokerException, InterruptedException { 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; }
ONEWAY:什么也不做,直接返回nullASYNC:异步方式,拉取成功或失败后,会在pullCallback对象中处理回调信息SYNC:同步方式,拉取的消息同步返回
private void pullMessageAsync( final String addr, final RemotingCommand request, final long timeoutMillis, final PullCallback pullCallback ) throws RemotingException, InterruptedException { this.remotingClient.invokeAsync(addr, request, timeoutMillis, new InvokeCallback() { @Override public void operationComplete(ResponseFuture responseFuture) { RemotingCommand response = responseFuture.getResponseCommand(); if (response != null) { try { PullResult pullResult = MQClientAPIImpl.this.processPullResponse(response, addr); assert pullResult != null; pullCallback.onSuccess(pullResult); } catch (Exception e) { pullCallback.onException(e); } } else { if (!responseFuture.isSendRequestOK()) { pullCallback.onException(new MQClientException("send request failed to " + addr + ". Request: " + request, responseFuture.getCause())); } else if (responseFuture.isTimeout()) { pullCallback.onException(new MQClientException("wait response from " + addr + " timeout :" + responseFuture.getTimeoutMillis() + "ms" + ". Request: " + request, responseFuture.getCause())); } else { pullCallback.onException(new MQClientException("unknown reason. addr: " + addr + ", timeoutMillis: " + timeoutMillis + ". Request: " + request, responseFuture.getCause())); } } } }); }
在InvokeCallback#operationComplete方法中,成功时会调用调用 pullCallback 的 onSuccess(...) 方法,失败时则调用 pullCallback 的 onException(...) 方法,接下来我们来看看pullCallback的内容。pullCallback对象是在DefaultMQPushConsumerImpl#pullMessage方法中创建并传入。
/** * 拉取消息的核心流程 * @param pullRequest */ public void pullMessage(final PullRequest pullRequest) { // 省略其他代码,重点关注 pullCallback ... // 消息拉取的回调函数,在拉取到消息后会进入这个方法处理 PullCallback pullCallback = new PullCallback() { @Override public void onSuccess(PullResult pullResult) { if (pullResult != null) { // 处理消息,将二制消息解码为java对象,也会对消息进行tag过滤 pullResult = DefaultMQPushConsumerImpl.this.pullAPIWrapper.processPullResult( pullRequest.getMessageQueue(), pullResult, subscriptionData); switch (pullResult.getPullStatus()) { case FOUND: ... long firstMsgOffset = Long.MAX_VALUE; if (pullResult.getMsgFoundList() == null || pullResult.getMsgFoundList().isEmpty()) { DefaultMQPushConsumerImpl.this .executePullRequestImmediately(pullRequest); } else { ... // 处理消息,处理顺序与并发消息 DefaultMQPushConsumerImpl.this.consumeMessageService .submitConsumeRequest( pullResult.getMsgFoundList(), processQueue, pullRequest.getMessageQueue(), dispatchToConsume); // 准备下一次的运行 if (DefaultMQPushConsumerImpl.this .defaultMQPushConsumer.getPullInterval() > 0) { DefaultMQPushConsumerImpl.this.executePullRequestLater(pullRequest, DefaultMQPushConsumerImpl.this .defaultMQPushConsumer.getPullInterval()); } else { DefaultMQPushConsumerImpl.this .executePullRequestImmediately(pullRequest); } } ... break; // 省略其他状态的处理 ... } } } @Override public void onException(Throwable e) { if (!pullRequest.getMessageQueue().getTopic() .startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) { log.warn("execute the pull request exception", e); } // 这个方法会把 pullRequest 丢到 pullRequestQueue 中 DefaultMQPushConsumerImpl.this .executePullRequestLater(pullRequest, pullTimeDelayMillsWhenException); } }; // 省略其他代码,重点关注 pullCallback ... }
PullCallback主要有两个方法:
onSuccess(...):拉取消息成功时调用,在这个方法里会解码消息,消费消息,然后准备下一次的pullQequest请求onException(...):拉取消息异常时调用,在这个方法里主要是将出现异常的pullQequest丢到pullRequestQueue,等待下一次再调用
PullAPIWrapper#processPullResultpublic PullResult processPullResult(final MessageQueue mq, final PullResult pullResult, final SubscriptionData subscriptionData) { PullResultExt pullResultExt = (PullResultExt) pullResult; this.updatePullFromWhichNode(mq, pullResultExt.getSuggestWhichBrokerId()); if (PullStatus.FOUND == pullResult.getPullStatus()) {
ByteBuffer byteBuffer = ByteBuffer.wrap(pullResultExt.getMessageBinary()); //二进制数据解码为对象 List<MessageExt> msgList = MessageDecoder.decodes(byteBuffer); List<MessageExt> msgListFilterAgain = msgList; if (!subscriptionData.getTagsSet().isEmpty() && !subscriptionData.isClassFilterMode()) { //tag过滤 msgListFilterAgain = new ArrayList<MessageExt>(msgList.size()); for (MessageExt msg : msgList) { if (msg.getTags() != null) { if (subscriptionData.getTagsSet().contains(msg.getTags())) { msgListFilterAgain.add(msg); } } } } if (this.hasHook()) { FilterMessageContext filterMessageContext = new FilterMessageContext(); filterMessageContext.setUnitMode(unitMode); filterMessageContext.setMsgList(msgListFilterAgain); this.executeHook(filterMessageContext); } for (MessageExt msg : msgListFilterAgain) { String traFlag = msg.getProperty(MessageConst.PROPERTY_TRANSACTION_PREPARED); if (Boolean.parseBoolean(traFlag)) { msg.setTransactionId(msg.getProperty(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX)); } MessageAccessor.putProperty(msg, MessageConst.PROPERTY_MIN_OFFSET, Long.toString(pullResult.getMinOffset())); MessageAccessor.putProperty(msg, MessageConst.PROPERTY_MAX_OFFSET, Long.toString(pullResult.getMaxOffset())); msg.setBrokerName(mq.getBrokerName()); } pullResultExt.setMsgFoundList(msgListFilterAgain); } pullResultExt.setMessageBinary(null); return pullResult; }
- 将二进制数据解码为对象,即将
byte[]解码为List<MessageExt> - 如果
consumer指定tag,则按tag进行过滤,其实就是调用Set#contains()判断tag是否符合条件 - 设置消息的属性,如
TransactionId、BrokerName
消费消息
消费消息的模式有两种:
ConsumeMessageConcurrentlyService:并发消费消息ConsumeMessageOrderlyService:顺序消费消息
并发消费消息,进入ConsumeMessageConcurrentlyService#submitConsumeRequest方法:DefaultMQPushConsumerImpl.this.consumeMessageService.submitConsumeRequest( pullResult.getMsgFoundList(), processQueue, pullRequest.getMessageQueue(), dispatchToConsume);
将获得到的消息封装为ConsumeRequest,然后提交到线程池中处理。在处理时,会判断消息的多少,如消息超过32条,就会对消息进行分页,每页都使用一个线程处理
public void submitConsumeRequest( final List<MessageExt> msgs, final ProcessQueue processQueue, final MessageQueue messageQueue, final boolean dispatchToConsume) { final int consumeBatchSize = this.defaultMQPushConsumer.getConsumeMessageBatchMaxSize(); // 一次只拉取32条数据,不足32条直接处理 if (msgs.size() <= consumeBatchSize) { ConsumeRequest consumeRequest = new ConsumeRequest(msgs, processQueue, messageQueue); try { // 添加任务 this.consumeExecutor.submit(consumeRequest); } catch (RejectedExecutionException e) { this.submitConsumeRequestLater(consumeRequest); } } else { // 超过32条就进行分页处理,每页都使用一个线程处理 for (int total = 0; total < msgs.size(); ) { List<MessageExt> msgThis = new ArrayList<MessageExt>(consumeBatchSize); for (int i = 0; i < consumeBatchSize; i++, total++) { if (total < msgs.size()) { msgThis.add(msgs.get(total)); } else { break; } } ConsumeRequest consumeRequest = new ConsumeRequest(msgThis, processQueue, messageQueue); try { this.consumeExecutor.submit(consumeRequest); } catch (RejectedExecutionException e) { for (; total < msgs.size(); total++) { msgThis.add(msgs.get(total)); } this.submitConsumeRequestLater(consumeRequest); } } } }
ConsumeRequest最终在线程池中执行
class ConsumeRequest implements Runnable { ... @Override public void run() { ... // 取出消息监听器 MessageListenerConcurrently listener = ConsumeMessageConcurrentlyService.this.messageListener; ... ConsumeReturnType returnType = ConsumeReturnType.SUCCESS; try { if (msgs != null && !msgs.isEmpty()) { for (MessageExt msg : msgs) { MessageAccessor.setConsumeStartTimeStamp( msg, String.valueOf(System.currentTimeMillis())); } } // 交由listener实际处理消息 status = listener.consumeMessage( Collections.unmodifiableList(msgs), context); } catch (Throwable e) { ... } ... // 处理结果 if (!processQueue.isDropped()) { ConsumeMessageConcurrentlyService.this.processConsumeResult(status, context, this); } else { log.warn(...); } } }
重要部分主要包含三个操作:
- 取出当前
consumer的消息监听器 - 执行消息监听器的
consumeMessage()方法 - 处理
consumeMessage()方法的返回值
执行完MessageListenerConcurrently#consumeMessage(...)方法后,接下来会处理这个方法的返回值,方法为ConsumeMessageConcurrentlyService#processConsumeResult
public void processConsumeResult( final ConsumeConcurrentlyStatus status, final ConsumeConcurrentlyContext context, final ConsumeRequest consumeRequest ) { // 省略重试的操作,后面分析重试机制时再详细展开 ... long offset = consumeRequest.getProcessQueue().removeMessage(consumeRequest.getMsgs()); if (offset >= 0 && !consumeRequest.getProcessQueue().isDropped()) { // 更新偏移量 this.defaultMQPushConsumerImpl.getOffsetStore() .updateOffset(consumeRequest.getMessageQueue(), offset, true); } }
下一次请求
// 准备下一次的运行 if (DefaultMQPushConsumerImpl.this .defaultMQPushConsumer.getPullInterval() > 0) { // 延迟 xxx 秒后进行一次 pullRequest DefaultMQPushConsumerImpl.this.executePullRequestLater(pullRequest, DefaultMQPushConsumerImpl.this .defaultMQPushConsumer.getPullInterval()); } else { // 立即进行一次 pullRequest DefaultMQPushConsumerImpl.this .executePullRequestImmediately(pullRequest); }
一个是延迟 xxx 秒后进行一次 pullRequest,另一个是立即进行一次 pullRequest
private void executePullRequestLater(final PullRequest pullRequest, final long timeDelay) { // 继续调用 this.mQClientFactory.getPullMessageService() .executePullRequestLater(pullRequest, timeDelay); } public void executePullRequestLater(final PullRequest pullRequest, final long timeDelay) { if (!isStopped()) { // 只执行一次,延迟执行 this.scheduledExecutorService.schedule(new Runnable() { @Override public void run() { // 调用的是 executePullRequestImmediately(...) PullMessageService.this.executePullRequestImmediately(pullRequest); } }, timeDelay, TimeUnit.MILLISECONDS); } else { log.warn("PullMessageServiceScheduledThread has shutdown"); } }
使用scheduledExecutorService来调用executePullRequestImmediately(...),需要注意的是,这个scheduledExecutorService只会执行一次,首次执行时间为指定的timeDelay后,也就是defaultMQPushConsumer.getPullInterval()的值
PullMessageService#executePullRequestImmediately方法,内容如下public void executePullRequestImmediately(final PullRequest pullRequest) { try { this.pullRequestQueue.put(pullRequest); } catch (InterruptedException e) { log.error("executePullRequestImmediately pullRequestQueue.put", e); } }
浙公网安备 33010602011771号