RocketMQ的消息发送方式主要含syncSend()同步发送、asyncSend()异步发送、sendOneWay()三种方式,sendOneWay()也是异步发送,区别在于不需等待Broker返回确认,

所以可能会存在信息丢失的状况,但吞吐量更高,具体需根据业务情况选用。

 

一个队列只会被消费组内的一个消费者消费,即如果topic相同,但是有多个consumerGroup,可能有A、B两个,那么此时一个队列会被A、B共同消费。只不过只会被A和B中各自的一个服务器消费。

详情见 https://codetd.com/article/12138580#3_54 中的第3点。

当实例化RocketMQTemplate过程中因为实现了InitializingBean接口,会执行afterPropertiesSet()方法

this.producer.start();

之后执行

public void start() throws MQClientException {
        this.defaultMQProducerImpl.start();
    。。。。。。。。。。。。
}

之后执行

public void start(boolean startFactory) throws MQClientException {
    .....................
    this.mQClientFactory.start();

    .....................

}

之后执行

MQClientInstance

public void start() throws MQClientException {
    
           //各种线程的start
          //NettyRemotingClient实现Netty客户器端功能,接受数据包,在客户器端处理后发送给服务端。
           this.mQClientAPIImpl.start();
          //获取nameserver的地址 默认每3秒更新Topic规则 清除下线的borken并且发送心跳到broken  持久化消费者进度 适配执行消费请求的线程池的核心线程书大小
                this.startScheduledTask();
          //启动消息拉取服务,循环拉取阻塞队列pullRequestQueue
                this.pullMessageService.start();
          //负载均衡服务开始
                this.rebalanceService.start();
                this.defaultMQProducer.getDefaultMQProducerImpl().start(false);
                this.log.info("the client factory [{}] start OK", this.clientId);
                this.serviceState = ServiceState.RUNNING;
}

 

this.pullMessageService.start()解析
1 public void run() {
 2         this.log.info(this.getServiceName() + " service started");
 3 
 4         while(!this.isStopped()) {
 5             try {
           //PullRequest是一个阻塞队列
 6                 PullRequest pullRequest = (PullRequest)this.pullRequestQueue.take();
 7                 this.pullMessage(pullRequest);
 8             } catch (InterruptedException var2) {
 9             } catch (Exception var3) {
10                 this.log.error("Pull Message Service Run Method exception", var3);
11             }
12         }
13 
14         this.log.info(this.getServiceName() + " service end");
15     }

 之后会执行拉取消息

1 public void pullMessage(final PullRequest pullRequest) {
      //校验队列是否被丢弃
  2         final ProcessQueue processQueue = pullRequest.getProcessQueue();
  3         if (processQueue.isDropped()) {
  4             log.info("the pull request[{}] is dropped.", pullRequest.toString());
  5             return;
  6         }
  7 
  8         pullRequest.getProcessQueue().setLastPullTimestamp(System.currentTimeMillis());
  9      //校验消费端的情况,状态是否正常,师傅暂停
 10         try {
 11             this.makeSureStateOK();
 12         } catch (MQClientException e) {
 13             log.warn("pullMessage exception, consumer state not ok", e);
 14             this.executePullRequestLater(pullRequest, pullTimeDelayMillsWhenException);
 15             return;
 16         }
 17 
 18         if (this.isPause()) {
 19             log.warn("consumer was paused, execute pull request later. instanceName={}, group={}", this.defaultMQPushConsumer.getInstanceName(), this.defaultMQPushConsumer.getConsumerGroup());
 20             this.executePullRequestLater(pullRequest, PULL_TIME_DELAY_MILLS_WHEN_SUSPEND);
 21             return;
 22         }
 23     //获取该队列中的消息数量
 24         long cachedMessageCount = processQueue.getMsgCount().get();
      //获取该队列中的消息大小(M)
 25         long cachedMessageSizeInMiB = processQueue.getMsgSize().get() / (1024 * 1024);
 26     //如果该队列中的消息数量>每个队列中最大的消息数量(默认1000)  这里做这些判断主要是进行流控,即rocketmq的削峰原理就在这里
 27         if (cachedMessageCount > this.defaultMQPushConsumer.getPullThresholdForQueue()) {
         //等待50ms再次去拉取,从这里可以看出实际上拉取消息是实时的,没有时间间隔的,
 28             this.executePullRequestLater(pullRequest, PULL_TIME_DELAY_MILLS_WHEN_FLOW_CONTROL);
          //该队列的流控次数每达到1000次则打印日志
 29             if ((queueFlowControlTimes++ % 1000) == 0) {
 30                 log.warn(
 31                     "the cached message count exceeds the threshold {}, so do flow control, minOffset={}, maxOffset={}, count={}, size={} MiB, pullRequest={}, flowControlTimes={}",
 32                     this.defaultMQPushConsumer.getPullThresholdForQueue(), processQueue.getMsgTreeMap().firstKey(), processQueue.getMsgTreeMap().lastKey(), cachedMessageCount, cachedMessageSizeInMiB, pullRequest, queueFlowControlTimes);
 33             }
 34             return;
 35         }
 36     //如果该队列中的消息大小(M)超过100M(默认),则执行流控,实现同上
 37         if (cachedMessageSizeInMiB > this.defaultMQPushConsumer.getPullThresholdSizeForQueue()) {
 38             this.executePullRequestLater(pullRequest, PULL_TIME_DELAY_MILLS_WHEN_FLOW_CONTROL);
 39             if ((queueFlowControlTimes++ % 1000) == 0) {
 40                 log.warn(
 41                     "the cached message size exceeds the threshold {} MiB, so do flow control, minOffset={}, maxOffset={}, count={}, size={} MiB, pullRequest={}, flowControlTimes={}",
 42                     this.defaultMQPushConsumer.getPullThresholdSizeForQueue(), processQueue.getMsgTreeMap().firstKey(), processQueue.getMsgTreeMap().lastKey(), cachedMessageCount, cachedMessageSizeInMiB, pullRequest, queueFlowControlTimes);
 43             }
 44             return;
 45         }
 46      //如果不是顺序消费,即并发消费
 47         if (!this.consumeOrderly) {
          //获取单队列并行消费允许的最大跨度   关键代码:这里用了读写锁保证并发 this.msgTreeMap.lastKey() - this.msgTreeMap.firstKey();
          /*

            RocketMQ是以consumer group+queue为单位是管理消费进度的,以一个consumer offset标记这个这个消费组在这条queue上的消费进度。
            如果某已存在的消费组出现了新消费实例的时候,依靠这个组的消费进度,就可以判断第一次是从哪里开始拉取的。
            每次消息成功后,本地的消费进度会被更新,然后由定时器定时同步到broker,以此持久化消费进度。

mq消费端做幂等原因:

            由于消费进度只是记录了一个下标,就可能出现拉取了100条消息如 2101-2200的消息,后面99条都消费结束了,只有2101消费一直没有结束的情况。
            在这种情况下,RocketMQ为了保证消息肯定被消费成功,消费进度职能维持在2101,直到2101也消费结束了,本地的消费进度才会一下子更新到2200。
            在这种设计下,就有消费大量重复的风险。如2101在还没有消费完成的时候消费实例突然退出(机器断电,或者被kill)。
            这条queue的消费进度还是维持在2101,当queue重新分配给新的实例的时候,新的实例从broker上拿到的消费进度还是维持在2101,
            这时候就会又从2101开始消费,2102-2200这批消息实际上已经被消费过还是会投递一次。

*/
          //单队列并行消费允许的最大跨度>2000(默认) 比如有1000条消息,offset=2这条消息一直没有消费完,但是其他都消费完了,但是会实时拉取消息,当多次拉取条消息
          // 直到offset=2003了,此时offset=2的这条还没有消费完成,mq就会认为有阻塞,会实行流控
 48             if (processQueue.getMaxSpan() > this.defaultMQPushConsumer.getConsumeConcurrentlyMaxSpan()) {
 49                 this.executePullRequestLater(pullRequest, PULL_TIME_DELAY_MILLS_WHEN_FLOW_CONTROL);
 50                 if ((queueMaxSpanFlowControlTimes++ % 1000) == 0) {
 51                     log.warn(
 52                         "the queue's messages, span too long, so do flow control, minOffset={}, maxOffset={}, maxSpan={}, pullRequest={}, flowControlTimes={}",
                //本次拉取的总消息数量中最小的offset 和最大的offset
 53                         processQueue.getMsgTreeMap().firstKey(), processQueue.getMsgTreeMap().lastKey(), processQueue.getMaxSpan(),
 54                         pullRequest, queueMaxSpanFlowControlTimes);
 55                 }
 56                 return;
 57             }
 58         } else {
          //顺序消费
 59             if (processQueue.isLocked()) {
 60                 if (!pullRequest.isLockedFirst()) {
 61                     final long offset = this.rebalanceImpl.computePullFromWhere(pullRequest.getMessageQueue());
 62                     boolean brokerBusy = offset < pullRequest.getNextOffset();
 63                     log.info("the first time to pull message, so fix offset from broker. pullRequest: {} NewOffset: {} brokerBusy: {}",
 64                         pullRequest, offset, brokerBusy);
 65                     if (brokerBusy) {
 66                         log.info("[NOTIFYME]the first time to pull message, but pull request offset larger than broker consume offset. pullRequest: {} NewOffset: {}",
 67                             pullRequest, offset);
 68                     }
 69 
 70                     pullRequest.setLockedFirst(true);
 71                     pullRequest.setNextOffset(offset);
 72                 }
 73             } else {
 74                 this.executePullRequestLater(pullRequest, pullTimeDelayMillsWhenException);
 75                 log.info("pull message later because not locked in broker, {}", pullRequest);
 76                 return;
 77             }
 78         }
 79      //获取该队列的topic、consumerGroup、tag信息
 80         final SubscriptionData subscriptionData = this.rebalanceImpl.getSubscriptionInner().get(pullRequest.getMessageQueue().getTopic());
 81         if (null == subscriptionData) {
 82             this.executePullRequestLater(pullRequest, pullTimeDelayMillsWhenException);
 83             log.warn("find the consumer's subscription failed, {}", pullRequest);
 84             return;
 85         }
 86 
 87         final long beginTimestamp = System.currentTimeMillis();
 88     //回调函数, 在这个方法被调用,里面的逻辑执行到onSuccess()时才会真正执行到这里  this.pullAPIWrapper.pullKernelImpl(。。。,pullCallback)
      //真正调用的地方:private void pullMessageAsync(....) -> pullCallback.onSuccess(pullResult);  pullCallback.onException(e); 
      //下面会对这个回调函数进行具体解析
89         PullCallback pullCallback = new PullCallback() {
 90           。。。。。。。。。。。。。。。。。。。。。191         };
192      
193         boolean commitOffsetEnable = false;
194         long commitOffsetValue = 0L;
        //如果是集群模式,此处读取方式是ReadOffsetType.READ_FROM_MEMORY 表示从内存中获取消费进度  也有从broken中获取进度 
195         if (MessageModel.CLUSTERING == this.defaultMQPushConsumer.getMessageModel()) {
196             commitOffsetValue = this.offsetStore.readOffset(pullRequest.getMessageQueue(), ReadOffsetType.READ_FROM_MEMORY);
197             if (commitOffsetValue > 0) {
198                 commitOffsetEnable = true;
199             }
200         }
201 
202         String subExpression = null;
203         boolean classFilter = false;
        //再次获取该队列的topic、consumerGroup、tag信息,之前已经获取了,不明白这里为啥要再次获取一次??
204         SubscriptionData sd = this.rebalanceImpl.getSubscriptionInner().get(pullRequest.getMessageQueue().getTopic());
205         if (sd != null) {
206             if (this.defaultMQPushConsumer.isPostSubscriptionWhenPull() && !sd.isClassFilterMode()) {
          //就是tag的值
207                 subExpression = sd.getSubString();
208             }
209 
210             classFilter = sd.isClassFilterMode();
211         }
212 
213         int sysFlag = PullSysFlag.buildSysFlag(
214             commitOffsetEnable, // commitOffset
215             true, // suspend
216             subExpression != null, // subscription
217             classFilter // class filter
218         );
      //拉取服务的真正实现入口:kernel:内核
219         try {
220             this.pullAPIWrapper.pullKernelImpl(
221                 pullRequest.getMessageQueue(),
222                 subExpression,
223                 subscriptionData.getExpressionType(),
224                 subscriptionData.getSubVersion(),
225                 pullRequest.getNextOffset(),
226                 this.defaultMQPushConsumer.getPullBatchSize(),
227                 sysFlag,
228                 commitOffsetValue,
229                 BROKER_SUSPEND_MAX_TIME_MILLIS,
230                 CONSUMER_TIMEOUT_MILLIS_WHEN_SUSPEND,
231                 CommunicationMode.ASYNC,
232                 pullCallback
233             );
234         } catch (Exception e) {
235             log.error("pullKernelImpl exception", e);
236             this.executePullRequestLater(pullRequest, pullTimeDelayMillsWhenException);
237         }
238     }

 

之后拉取消息的核心实现:

1  public PullResult pullKernelImpl(
 2         final MessageQueue mq,
 3         final String subExpression,
 4         final String expressionType,
 5         final long subVersion,
 6         final long offset,
 7         final int maxNums,
 8         final int sysFlag,
 9         final long commitOffset,
10         final long brokerSuspendMaxTimeMillis,
11         final long timeoutMillis,
12         final CommunicationMode communicationMode,
13         final PullCallback pullCallback
14     ) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
      //获取broker的地址
15         FindBrokerResult findBrokerResult =
16             this.mQClientFactory.findBrokerAddressInSubscribe(mq.getBrokerName(),
17                 this.recalculatePullFromWhichNode(mq), false);
18         if (null == findBrokerResult) {
19             this.mQClientFactory.updateTopicRouteInfoFromNameServer(mq.getTopic());
20             findBrokerResult =
21                 this.mQClientFactory.findBrokerAddressInSubscribe(mq.getBrokerName(),
22                     this.recalculatePullFromWhichNode(mq), false);
23         }
24 
25         if (findBrokerResult != null) {
26             {
27                 // check version
28                 if (!ExpressionType.isTagType(expressionType)
29                     && findBrokerResult.getBrokerVersion() < MQVersion.Version.V4_1_0_SNAPSHOT.ordinal()) {
30                     throw new MQClientException("The broker[" + mq.getBrokerName() + ", "
31                         + findBrokerResult.getBrokerVersion() + "] does not upgrade to support for filter message by " + expressionType, null);
32                 }
33             }
34             int sysFlagInner = sysFlag;
35        //如果是备用机器??
36             if (findBrokerResult.isSlave()) {
37                 sysFlagInner = PullSysFlag.clearCommitOffsetFlag(sysFlagInner);
38             }
39        //通过netty拉取信息,这里组装请求信息
40             PullMessageRequestHeader requestHeader = new PullMessageRequestHeader();
41             requestHeader.setConsumerGroup(this.consumerGroup);
42             requestHeader.setTopic(mq.getTopic());
43             requestHeader.setQueueId(mq.getQueueId());
44             requestHeader.setQueueOffset(offset);
45             requestHeader.setMaxMsgNums(maxNums);//一次拉取的最大数据条数
46             requestHeader.setSysFlag(sysFlagInner);
47             requestHeader.setCommitOffset(commitOffset);
48             requestHeader.setSuspendTimeoutMillis(brokerSuspendMaxTimeMillis);
49             requestHeader.setSubscription(subExpression);
50             requestHeader.setSubVersion(subVersion);
51             requestHeader.setExpressionType(expressionType);
52 
53             String brokerAddr = findBrokerResult.getBrokerAddr();
54             if (PullSysFlag.hasClassFilterFlag(sysFlagInner)) {
55                 brokerAddr = computPullFromWhichFilterServer(mq.getTopic(), brokerAddr);
56             }
57 
58             PullResult pullResult = this.mQClientFactory.getMQClientAPIImpl().pullMessage(
59                 brokerAddr,
60                 requestHeader,
61                 timeoutMillis,
62                 communicationMode,
63                 pullCallback);
64 
65             return pullResult;
66         }
67 
68         throw new MQClientException("The broker[" + mq.getBrokerName() + "] not exist", null);
69     }

 异步拉取:

1  private void pullMessageAsync(
 2         final String addr,
 3         final RemotingCommand request,
 4         final long timeoutMillis,
 5         final PullCallback pullCallback
 6     ) throws RemotingException, InterruptedException {
      //异步通过netty进行异步调用 
7         this.remotingClient.invokeAsync(addr, request, timeoutMillis, new InvokeCallback() {
      //拉取消息回调后会执行operationComplete() 
8             @Override
 9             public void operationComplete(ResponseFuture responseFuture) {
10                 RemotingCommand response = responseFuture.getResponseCommand();
11                 if (response != null) {
12                     try {
                //获取拉取的原始结果,这个pullResult里面就有拉取的数据
13                         PullResult pullResult = MQClientAPIImpl.this.processPullResponse(response);
14                         assert pullResult != null;
                //这里才会真正执行到回调函数,具体解析详见下面
15                         pullCallback.onSuccess(pullResult);
16                     } catch (Exception e) {
17                         pullCallback.onException(e);
18                     }
19                 } else {
20                     if (!responseFuture.isSendRequestOK()) {
21                         pullCallback.onException(new MQClientException("send request failed to " + addr + ". Request: " + request, responseFuture.getCause()));
22                     } else if (responseFuture.isTimeout()) {
23                         pullCallback.onException(new MQClientException("wait response from " + addr + " timeout :" + responseFuture.getTimeoutMillis() + "ms" + ". Request: " + request,
24                             responseFuture.getCause()));
25                     } else {
26                         pullCallback.onException(new MQClientException("unknown reason. addr: " + addr + ", timeoutMillis: " + timeoutMillis + ". Request: " + request, responseFuture.getCause()));
27                     }
28                 }
29             }
30         });
31     }

 

拉取完成后执行回调函数:

1     PullCallback pullCallback = new PullCallback() {
  2             @Override
  3             public void onSuccess(PullResult pullResult) {
  4                 if (pullResult != null) {
              //详细解析见下面
  5                     pullResult = DefaultMQPushConsumerImpl.this.pullAPIWrapper.processPullResult(pullRequest.getMessageQueue(), pullResult,
  6                         subscriptionData);
  7 
  8                     switch (pullResult.getPullStatus()) {
  9                         case FOUND:
 10                             long prevRequestOffset = pullRequest.getNextOffset();
 11                             pullRequest.setNextOffset(pullResult.getNextBeginOffset());
 12                             long pullRT = System.currentTimeMillis() - beginTimestamp;
 13                             DefaultMQPushConsumerImpl.this.getConsumerStatsManager().incPullRT(pullRequest.getConsumerGroup(),
 14                                 pullRequest.getMessageQueue().getTopic(), pullRT);
 15 
 16                             long firstMsgOffset = Long.MAX_VALUE;
 17                             if (pullResult.getMsgFoundList() == null || pullResult.getMsgFoundList().isEmpty()) {
 18                                 DefaultMQPushConsumerImpl.this.executePullRequestImmediately(pullRequest);
 19                             } else {
 20                                 firstMsgOffset = pullResult.getMsgFoundList().get(0).getQueueOffset();
 21 
 22                                 DefaultMQPushConsumerImpl.this.getConsumerStatsManager().incPullTPS(pullRequest.getConsumerGroup(),
 23                                     pullRequest.getMessageQueue().getTopic(), pullResult.getMsgFoundList().size());
 24                   //判断是否能发送给消费者,并发没有用到这个参数
 25                                 boolean dispatchToConsume = processQueue.putMessage(pullResult.getMsgFoundList());
 26                                 DefaultMQPushConsumerImpl.this.consumeMessageService.submitConsumeRequest(
 27                                     pullResult.getMsgFoundList(),
 28                                     processQueue,
 29                                     pullRequest.getMessageQueue(),
 30                                     dispatchToConsume);
 31 
 32                                 if (DefaultMQPushConsumerImpl.this.defaultMQPushConsumer.getPullInterval() > 0) {
 33                                     DefaultMQPushConsumerImpl.this.executePullRequestLater(pullRequest,
 34                                         DefaultMQPushConsumerImpl.this.defaultMQPushConsumer.getPullInterval());
 35                                 } else {
 36                                     DefaultMQPushConsumerImpl.this.executePullRequestImmediately(pullRequest);
 37                                 }
 38                             }
 39 
 40                             if (pullResult.getNextBeginOffset() < prevRequestOffset
 41                                 || firstMsgOffset < prevRequestOffset) {
 42                                 log.warn(
 43                                     "[BUG] pull message result maybe data wrong, nextBeginOffset: {} firstMsgOffset: {} prevRequestOffset: {}",
 44                                     pullResult.getNextBeginOffset(),
 45                                     firstMsgOffset,
 46                                     prevRequestOffset);
 47                             }
 48 
 49                             break;
 50                         case NO_NEW_MSG:
 51                             pullRequest.setNextOffset(pullResult.getNextBeginOffset());
 52 
 53                             DefaultMQPushConsumerImpl.this.correctTagsOffset(pullRequest);
 54 
 55                             DefaultMQPushConsumerImpl.this.executePullRequestImmediately(pullRequest);
 56                             break;
 57                         case NO_MATCHED_MSG:
 58                             pullRequest.setNextOffset(pullResult.getNextBeginOffset());
 59 
 60                             DefaultMQPushConsumerImpl.this.correctTagsOffset(pullRequest);
 61 
 62                             DefaultMQPushConsumerImpl.this.executePullRequestImmediately(pullRequest);
 63                             break;
 64                         case OFFSET_ILLEGAL:
 65                             log.warn("the pull request offset illegal, {} {}",
 66                                 pullRequest.toString(), pullResult.toString());
 67                             pullRequest.setNextOffset(pullResult.getNextBeginOffset());
 68 
 69                             pullRequest.getProcessQueue().setDropped(true);
 70                             DefaultMQPushConsumerImpl.this.executeTaskLater(new Runnable() {
 71 
 72                                 @Override
 73                                 public void run() {
 74                                     try {
 75                                         DefaultMQPushConsumerImpl.this.offsetStore.updateOffset(pullRequest.getMessageQueue(),
 76                                             pullRequest.getNextOffset(), false);
 77 
 78                                         DefaultMQPushConsumerImpl.this.offsetStore.persist(pullRequest.getMessageQueue());
 79 
 80                                         DefaultMQPushConsumerImpl.this.rebalanceImpl.removeProcessQueue(pullRequest.getMessageQueue());
 81 
 82                                         log.warn("fix the pull request offset, {}", pullRequest);
 83                                     } catch (Throwable e) {
 84                                         log.error("executeTaskLater Exception", e);
 85                                     }
 86                                 }
 87                             }, 10000);
 88                             break;
 89                         default:
 90                             break;
 91                     }
 92                 }
 93             }
 94 
 95             @Override
 96             public void onException(Throwable e) {
 97                 if (!pullRequest.getMessageQueue().getTopic().startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) {
 98                     log.warn("execute the pull request exception", e);
 99                 }
100 
101                 DefaultMQPushConsumerImpl.this.executePullRequestLater(pullRequest, pullTimeDelayMillsWhenException);
102             }
103         };



解析:public PullResult processPullResult(。。。。)主要是讲原始消息解析成 MessageExt
1     public PullResult processPullResult(final MessageQueue mq, final PullResult pullResult,
 2         final SubscriptionData subscriptionData) {
 3         PullResultExt pullResultExt = (PullResultExt) pullResult;
 4      //更新mq(topic、队列id、broker)和brokerId的对应关系
 5         this.updatePullFromWhichNode(mq, pullResultExt.getSuggestWhichBrokerId());
      //如果拉取到了数据
 6         if (PullStatus.FOUND == pullResult.getPullStatus()) {
      //解析拉取的数据  
 7             ByteBuffer byteBuffer = ByteBuffer.wrap(pullResultExt.getMessageBinary());
 8             List<MessageExt> msgList = MessageDecoder.decodes(byteBuffer);
 9 
10             List<MessageExt> msgListFilterAgain = msgList;
11             if (!subscriptionData.getTagsSet().isEmpty() && !subscriptionData.isClassFilterMode()) {
12                 msgListFilterAgain = new ArrayList<MessageExt>(msgList.size());
13                 for (MessageExt msg : msgList) {
14                     if (msg.getTags() != null) {
                //再次过滤  tagsSet是什么??
15                         if (subscriptionData.getTagsSet().contains(msg.getTags())) {
16                             msgListFilterAgain.add(msg);
17                         }
18                     }
19                 }
20             }
21       //钩子,目前没有实现,可扩展
22             if (this.hasHook()) {
23                 FilterMessageContext filterMessageContext = new FilterMessageContext();
24                 filterMessageContext.setUnitMode(unitMode);
25                 filterMessageContext.setMsgList(msgListFilterAgain);
26                 this.executeHook(filterMessageContext);
27             }
28        //MessageExt中属性塞入一些值
29             for (MessageExt msg : msgListFilterAgain) {
30                 String traFlag = msg.getProperty(MessageConst.PROPERTY_TRANSACTION_PREPARED);
31                 if (Boolean.parseBoolean(traFlag)) {
32                     msg.setTransactionId(msg.getProperty(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX));
33                 }
34                 MessageAccessor.putProperty(msg, MessageConst.PROPERTY_MIN_OFFSET,
35                     Long.toString(pullResult.getMinOffset()));
36                 MessageAccessor.putProperty(msg, MessageConst.PROPERTY_MAX_OFFSET,
37                     Long.toString(pullResult.getMaxOffset()));
38                 msg.setBrokerName(mq.getBrokerName());
39             }
40     
41             pullResultExt.setMsgFoundList(msgListFilterAgain);
42         }
43 
44         pullResultExt.setMessageBinary(null);
45 
46         return pullResult;
47     }

 

解析:DefaultMQPushConsumerImpl.this.consumeMessageService.submitConsumeRequest(.....)
1     public void submitConsumeRequest(
 2         final List<MessageExt> msgs,
 3         final ProcessQueue processQueue,
 4         final MessageQueue messageQueue,
 5         final boolean dispatchToConsume) {
      //消费者一次可以消费的最大数量
 6         final int consumeBatchSize = this.defaultMQPushConsumer.getConsumeMessageBatchMaxSize();
 7         if (msgs.size() <= consumeBatchSize) {
 8             ConsumeRequest consumeRequest = new ConsumeRequest(msgs, processQueue, messageQueue);
 9             try {
10                 this.consumeExecutor.submit(consumeRequest);
11             } catch (RejectedExecutionException e) {
12                 this.submitConsumeRequestLater(consumeRequest);
13             }
14         } else {
        //如果拉取的总的消息数量msgs.size有8笔,consumeBatchSize=2,则分4次去消费
15             for (int total = 0; total < msgs.size(); ) {
16                 List<MessageExt> msgThis = new ArrayList<MessageExt>(consumeBatchSize);
17                 for (int i = 0; i < consumeBatchSize; i++, total++) {
18                     if (total < msgs.size()) {
19                         msgThis.add(msgs.get(total));
20                     } else {
21                         break;
22                     }
23                 }
24         //通过线程池执行ConsumeRequest的run()
25                 ConsumeRequest consumeRequest = new ConsumeRequest(msgThis, processQueue, messageQueue);
26                 try {
27                     this.consumeExecutor.submit(consumeRequest);
28                 } catch (RejectedExecutionException e) {
29                     for (; total < msgs.size(); total++) {
30                         msgThis.add(msgs.get(total));
31                     }
32 
33                     this.submitConsumeRequestLater(consumeRequest);
34                 }
35             }
36         }
37     }

解析:ConsumeRequest的run() 这里面会调用listen.consumeMessage(....) 这个方法就是实际上我们业务代码中拿到消费信息的方法
1  public void run() {
    //如果该队列被丢弃,直接返回
 2     if (this.processQueue.isDropped()) {
 3                 ConsumeMessageConcurrentlyService.log.info("the message queue not be able to consume, because it's dropped. group={} {}", ConsumeMessageConcurrentlyService.this.consumerGroup, this.messageQueue);
 4             } else {
 5                 int exeSize = this.msgs.size();
 6                 MessageListenerConcurrently listener = ConsumeMessageConcurrentlyService.this.messageListener;
 7                 ConsumeConcurrentlyContext context = new ConsumeConcurrentlyContext(this.messageQueue);
 8                 ConsumeConcurrentlyStatus status = null;
 9                 ConsumeMessageContext consumeMessageContext = null;
           //作用??
10                 if (ConsumeMessageConcurrentlyService.this.defaultMQPushConsumerImpl.hasHook()) {
11                     consumeMessageContext = new ConsumeMessageContext();
12                     consumeMessageContext.setConsumerGroup(ConsumeMessageConcurrentlyService.this.defaultMQPushConsumer.getConsumerGroup());
13                     consumeMessageContext.setProps(new HashMap());
14                     consumeMessageContext.setMq(this.messageQueue);
15                     consumeMessageContext.setMsgList(this.msgs);
16                     consumeMessageContext.setSuccess(false);
17                     ConsumeMessageConcurrentlyService.this.defaultMQPushConsumerImpl.executeHoefore(consumeMessageContext);
18                 }
19 
20                 long beginTimestamp = System.currentTimeMillis();
21                 boolean hasException = false;
22                 ConsumeReturnType returnType = ConsumeReturnType.SUCCESS;
23 
24                 try {
25                     ConsumeMessageConcurrentlyService.this.resetRetryTopic(this.msgs);
26                     if (this.msgs != null && !this.msgs.isEmpty()) {
27                         Iterator var10 = this.msgs.iterator();
28 
29                         while(var10.hasNext()) {
30                             MessageExt msg = (MessageExt)var10.next();
31                             MessageAccessor.setConsumeStartTimeStamp(msg, String.valueOf(System.currentTimeMillis()));
32                         }
33                     }
34 
35                     if (exeSize == 1) {
36                         MQContextHelper.readChainContextFrom((Message)this.msgs.get(0), true);
37                     }
38             //这个listener的实际对象由DefaultMQPushConsumer传入,所以这里会调用到DefaultMQPushConsumer中的接口实现方法中
39                     status = listener.consumeMessage(Collections.unmodifiableList(tis.msgs), context);
40                 } catch (Throwable var12) {
41                     ConsumeMessageConcurrentlyService.log.warn("consumeMessage exception: {} Group: {} Msgs: {} MQ: {}", new Object[]{RemotingHelper.exceptionSimpleDesc(var12), ConsumeMessageConcurrentlyService.this.consumerGroup, this.msgs, this.messageQueue});
42                     hasException = true;
43                 }
44 
45                 long consumeRT = System.currentTimeMillis() - beginTimestamp;
46                 if (null == status) {
47                     if (hasException) {
48                         returnType = ConsumeReturnType.EXCEPTION;
49                     } else {
50                         returnType = ConsumeReturnType.RETURNNULL;
51                     }
52                 } else if (consumeRT >= ConsumeMessageConcurrentlyService.this.defaultMQPushConsumer.getConsumeTimeout() * 60L * 1000L) {
53                     returnType = ConsumeReturnType.TIME_OUT;
54                 } else if (ConsumeConcurrentlyStatus.RECONSUME_LATER == status) {
55                     returnType = ConsumeReturnType.FAILED;
56                 } else if (ConsumeConcurrentlyStatus.CONSUME_SUCCESS == status) {
57                     returnType = ConsumeReturnType.SUCCESS;
58                 }
59 
60                 if (ConsumeMessageConcurrentlyService.this.defaultMQPushConsumerImpl.hasHook()) {
61                     consumeMessageContext.getProps().put("ConsumeContextType", returnType.name());
62                 }
63 
64                 if (null == status) {
65                     ConsumeMessageConcurrentlyService.log.warn("consumeMessage return null, Group: {} Msgs: {} MQ: {}", new Object[]{ConsumeMessageConcurrentlyService.this.consumerGroup, this.msgs, this.messageQueue});
66                     status = ConsumeConcurrentlyStatus.RECONSUME_LATER;
67                 }
68 
69                 if (ConsumeMessageConcurrentlyService.this.defaultMQPushConsumerImpl.hasHook()) {
70                     consumeMessageContext.setStatus(status.toString());
71                     consumeMessageContext.setSuccess(ConsumeConcurrentlyStatus.CONSUME_SUCCESS == status);
72                     ConsumeMessageConcurrentlyService.this.defaultMQPushConsumerImpl.executeHookAfter(consumeMessageContext);
73                 }
74 
75                 ConsumeMessageConcurrentlyService.this.getConsumerStatsManager().incConsumeRT(ConsumeMessageConcurrentlyService.this.consumerGroup, this.messageQueue.getTopic(), consumeRT);
76                 if (!this.processQueue.isDropped()) {
              //处理消费结果 详见下面解析
77                     ConsumeMessageConcurrentlyService.this.processConsumeResult(status, context, this);
78                 } else {
79                     ConsumeMessageConcurrentlyService.log.warn("processQueue is dropped without process consume result. messageQueue={}, msgs={}", this.messageQueue, this.msgs);
80                 }
81 
82             }
83 
84  }


解析:processConsumeResult(....) 处理消费结果  这个方法里面会对消费失败的进行消息重试   (生产发送失败也可以重试,但只限于同步发送的情况下)
在确定是否需要重试的时候,进一步处理哪些消息需要重试,也就是哪些消息会发送回broker
1 public void processConsumeResult(
 2         final ConsumeConcurrentlyStatus status,
 3         final ConsumeConcurrentlyContext context,
 4         final ConsumeRequest consumeRequest
 5     ) {
      //默认是int类型能存储的最大值
 6         int ackIndex = context.getAckIndex();
 7      //判断拉取的消息数量是否为空
 8         if (consumeRequest.getMsgs().isEmpty())
 9             return;
10 
11         switch (status) {
12             case CONSUME_SUCCESS:
13                 if (ackIndex >= consumeRequest.getMsgs().size()) {
14                     ackIndex = consumeRequest.getMsgs().size() - 1;
15                 }
16                 int ok = ackIndex + 1;
17                 int failed = consumeRequest.getMsgs().size() - ok;
            //记录消费成功或者失败的吞吐量?? TPS
18                 this.getConsumerStatsManager().incConsumeOKTPS(consumerGroup, consumeRequest.getMessageQueue().getTopic(), ok);
19                 this.getConsumerStatsManager().incConsumeFailedTPS(consumerGroup, consumeRequest.getMessageQueue().getTopic(), failed);
20                 break;
21             case RECONSUME_LATER:
22                 ackIndex = -1;
23                 this.getConsumerStatsManager().incConsumeFailedTPS(consumerGroup, consumeRequest.getMessageQueue().getTopic(),
24                     consumeRequest.getMsgs().size());
25                 break;
26             default:
27                 break;
28         }
29 
30         switch (this.defaultMQPushConsumer.getMessageModel()) {
31             case BROADCASTING:
32                 for (int i = ackIndex + 1; i < consumeRequest.getMsgs().size(); i++) {
33                     MessageExt msg = consumeRequest.getMsgs().get(i);
34                     log.warn("BROADCASTING, the message consume failed, drop it, {}", msg.toString());
35                 }
36                 break;
        //一般使用集群模式
37             case CLUSTERING:
38                 List<MessageExt> msgBackFailed = new ArrayList<MessageExt>(consumeRequest.getMsgs().size());
          //正常情况下ackIndex会在上面的代码中被赋值为consumeRequest.getMsgs().size()
           //所以这里的for循环就可以=for(int i=3;i<3;i++)这种情况了,根本就不会进行循环,也就不会进行消息重试了
39                 for (int i = ackIndex + 1; i < consumeRequest.getMsgs().size(); i++) {
40                     MessageExt msg = consumeRequest.getMsgs().get(i);
              //直接重新发送消息到broker  这个方法的解析流程具体可以看:
              //如果msgs有一条消息要重试,则msgs中的所有消息都要重试
41                     boolean result = this.sendMessageBack(msg, context);
42                     if (!result) {
43                         msg.setReconsumeTimes(msg.getReconsumeTimes() + 1);
44                         msgBackFailed.add(msg);
45                     }
46                 }
47 
48                 if (!msgBackFailed.isEmpty()) {
49                     consumeRequest.getMsgs().removeAll(msgBackFailed);
50             //如果发送消息失败,则直接再次消费
51                     this.submitConsumeRequestLater(msgBackFailed, consumeRequest.getProcessQueue(), consumeRequest.getMessageQueue());
52                 }
53                 break;
54             default:
55                 break;
56         }

       // 这篇文章讲了很多下面代码的实现
      //removeMessage解析见如下
58         long offset = consumeRequest.getProcessQueue().removeMessage(consumeRequest.getMsgs());
       //更新offset,之后就从此offset开始消费 ,这里也是会导致重复消费的地方,详见上面链接中的文章
59         if (offset >= 0 && !consumeRequest.getProcessQueue().isDropped()) {
60             this.defaultMQPushConsumerImpl.getOffsetStore().updateOffset(consumeRequest.getMessageQueue(), offset, true);
61         }
62     }

 

解析removeMessage

1  public long removeMessage(final List<MessageExt> msgs) {
 2         long result = -1;
 3         final long now = System.currentTimeMillis();
 4         try {
 5             this.lockTreeMap.writeLock().lockInterruptibly();
 6             this.lastConsumeTimestamp = now;
 7             try {
           //本次从一个队列中拉取的消息总数,不只是这一次消费的数量
 8                 if (!msgTreeMap.isEmpty()) {
              //默认下次从该队列中最大的一个offset中再+1进行 比如本次拉取的队列中的消息的offset是1000,则下次默认从1001开始
 9                     result = this.queueOffsetMax + 1;
10                     int removedCnt = 0;
11                     for (MessageExt msg : msgs) {
                //已经消费完成的就从msgTreeMap中删除掉
12                         MessageExt prev = msgTreeMap.remove(msg.getQueueOffset());
13                         if (prev != null) {
14                             removedCnt--;
15                             msgSize.addAndGet(0 - msg.getBody().length);
16                         }
17                     }
18                     msgCount.addAndGet(removedCnt);
19             //如果本次从队列中一共拉取了10条,但是最大消费数量为2 ,那么此时的msgTreeMap=8
20                     if (!msgTreeMap.isEmpty()) {
                //获取剩余未消费的消息集合中的最小的offset,可能拉取了100条 201-300都消费完成了,但是200还没有,
                 //那么此时的result=200,如果此时断电了,那么消费的进度就维持在了200,就会造成重复消费。
21                         result = msgTreeMap.firstKey();
22                     }
23                 }
24             } finally {
25                 this.lockTreeMap.writeLock().unlock();
26             }
27         } catch (Throwable t) {
28             log.error("removeMessage exception", t);
29         }
30 
31         return result;
32     }
msgTreeMap