看一下客户端收到消息后的处理:

MQClientAPIImpl#processPullResponse

private PullResult processPullResponse(
final RemotingCommand response) throws MQBrokerException, RemotingCommandException {
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());
} PullMessageResponseHeader responseHeader =
(PullMessageResponseHeader) response.decodeCommandCustomHeader(PullMessageResponseHeader.class); return new PullResultExt(pullStatus, responseHeader.getNextBeginOffset(), responseHeader.getMinOffset(),
responseHeader.getMaxOffset(), null, responseHeader.getSuggestWhichBrokerId(), response.getBody());
}

得到PullResult对象后,MQClientAPIImpl#pullMessageAsyncm,回调pullCallback.onSuccess():

 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);
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()));
}
}
}
});
}

PullCallback#onSuccess:

调用pullAPIWrapper的processPullResult将消息字节属组解码成消息列表填充msgFoundList,并对消息进行消息过滤(TAG模式)。

public class PullResult {
   private final PullStatus pullStatus;//拉取结果
private final long nextBeginOffset;  //下次拉取偏移量

private final long minOffset;  //消息队列最小偏移量
private final long maxOffset;  //消息队列最大偏移量
private List<MessageExt> msgFoundList;//具体拉取的消息列表

回到onSuccess

可以看到这里如果消息列表为空 马上进行了重试,为什么PullStatus.FOUND,msgFoundList还会为空呢?因为在RocketMQ根据TAG消息过滤,在服务端只是验证了TAG的hashcode,在客户端再次对消息进行过滤,故可能出现msgFoundList为空的情况。

else:

将拉取的消息提交到ConsumeMessageService中公消费者消费(异步)。

根据pullInterval参数,等待pullInterval毫秒后将PullRequest对象放入到PullMessageService的pullRequestQueue中,该消息队列的下次拉取即将被激活,达到持续消息拉取,实现准实时消息拉取的效果。

异常码处理:

NO_NEW_MSG和NO_MATCHED_MSG会直接使用服务端矫正后的偏移量进行一次重试。

OFFSET_ILLEGAL:

case OFFSET_ILLEGAL:
log.warn("the pull request offset illegal, {} {}",
pullRequest.toString(), pullResult.toString());
pullRequest.setNextOffset(pullResult.getNextBeginOffset()); pullRequest.getProcessQueue().setDropped(true);//丢弃该消费队列,ProcessQueue中拉取的消息将停止消费
DefaultMQPushConsumerImpl.this.executeTaskLater(new Runnable() {//更新消息的消费进度,并持久化,并将该消息队列从rebalanceImpl
                    
try {
DefaultMQPushConsumerImpl.this.offsetStore.updateOffset(pullRequest.getMessageQueue(),
pullRequest.getNextOffset(), false);

DefaultMQPushConsumerImpl.this.offsetStore.persist(pullRequest.getMessageQueue());

DefaultMQPushConsumerImpl.this.rebalanceImpl.removeProcessQueue(pullRequest.getMessageQueue());

log.warn("fix the pull request offset, {}", pullRequest);
} catch (Throwable e) {
log.error("executeTaskLater Exception", e);
}
}
}, 10000);
break;
 
 

【mq读书笔记】客户端处理消息(回调提交到异步业务线程池,pullRequest重新入队)的更多相关文章

  1. 【mq读书笔记】顺序消息

    注意异常情况导致整个消费无限重试 阻塞消费 mq支持局部消息顺序消费,可以确保同一个消息消费队列中的消息被顺序消费.看下针对顺序消息在整个消费过程中做的调整: 队列负载: DefaultMQPushC ...

  2. 【mq读书笔记】定时消息

    mq不支持任意的时间京都,如果要支持,不可避免的需要在Broker层做消息排序,加上持久化方面的考量,将不可避免地带来巨大的性能消耗,所以rocketMQ只支持特定级别的延迟消息. 在Broker短通 ...

  3. 【mq读书笔记】消息拉取

    疑问:PullRequest何时添加? PullMessageService提供延迟添加与立即添加2种方式 疑问:PullRequest是在什么时候创建的呢? 1.上上图中 PullRequest p ...

  4. 【mq读书笔记】mq消息消费

    消息消费以组的的模式开展: 一个消费组内可以包含多个消费者,每一个消费组可订阅多个主题: 消费组之间有集群模式与广播模式两种消费模式:集群模式-主题下的同一条消息只允许被其中一个消费者消费.广播模式- ...

  5. TJI读书笔记14-闭包与回调

      TJI读书笔记14-闭包与回调 闭包与回调 为什么要使用内部类?内部类继承自某个类或者实现某个接口,内部类的代码可以操作外嵌类的对象. 这不是使用内部类的理由. 那么为什么使用内部类呢? 我觉得如 ...

  6. 【mq读书笔记】消息确认(失败消息,定时队列重新消费)

    接上文的集群模式,监听器返回RECONSUME_LATER,需要将将这些消息发送给Broker延迟消息.如果发送ack消息失败,将延迟5s后提交线程池进行消费. 入口:ConsumeMessageCo ...

  7. 【mq读书笔记】消息拉取长轮训机制(Broker端)

    RocketMQ并没有真正实现推模式,而是消费者主动想消息服务器拉取消息,推模式是循环向消息服务端发送消息拉取请求. 如果消息消费者向RocketMQ发送消息拉取时,消息未到达消费队列: 如果不启用长 ...

  8. 【mq读书笔记】消息消费过程(钩子 失败重试 消费偏移记录)

    在https://www.cnblogs.com/lccsblog/p/12249265.html中,PullMessageService负责对消息队列进行消息拉取,从远端服务器拉取消息后将消息存入P ...

  9. 【mq读书笔记】消息到达唤醒挂起线程检查新消息

    DefaultMessageStore#start 当新消息到达CommitLog是,ReputMessageService线程负责将消息转发给ConsumeQueue,IndexFile,如果Bro ...

随机推荐

  1. Windows 端口被占用,但进程号对应的进程不存在,使用Get-Process来查找进程挺方便的

    Windows上很少安装数据库,这次遇到一个小问题:数据库启动之后提示: 警告: 无法为 "*" 创建监听套接字 致命错误: 无法创建TCP/IP套接字 日志: 数据库系统已关闭 ...

  2. typeerror object of type ‘decimal‘ is not json serializable jsonify

    当使用flask的jsonify返回json数据时,由于数据库有些字段类型使用decimal,而jsonify无法处理 解决方案 导入下面的包即可解决 pip install simplejson

  3. 《Clojure编程》笔记 第5章 宏

    目录 背景简述 第5章 宏 5.0 术语 5.1 宏到底是什么 5.1.1 宏不是什么 5.1.2 有什么是宏能做而函数不能做的 5.1.3 宏vsRuby的eval 5.2 编写你的第一个宏 5.3 ...

  4. Git clone 克隆Github上的仓库,速度慢?

    一. 终端输入: git config --global http.postBuffer 524288000 二. git替换https 三. windows 安装 https://motrix.ap ...

  5. C++ 有用的资源

    C++ 有用的资源 以下资源包含了 C++ 有关的网站.书籍和文章.请使用它们来进一步学习 C++ 的知识. C++ 有用的网站 C++ Programming Language Tutorials ...

  6. [MIT6.006] 9. Table Doubling, Karp-Rabin 双散列表, Karp-Rabin

    在整理课程笔记前,先普及下课上没细讲的东西,就是下图,如果有个操作g(x),它最糟糕的时间复杂度为Ο(c2 * n),它最好时间复杂度是Ω(c1 * n),那么θ则为Θ(n).简单来说:如果O和Ω可以 ...

  7. string.contains()

    public class test { public static void main(String[] args){ System.out.println("abcde".con ...

  8. tcpack---1简述

    TCP重传机制 TCP要保证所有的数据包都可以到达,所以,必需要有重传机制. 超时重传机制 一种是不回ack,死等3,当发送方发现收不到3的ack超时后,会重传3.一旦接收方收到3后,会ack 回 4 ...

  9. binary hacks读数笔记(nm命令)

    nm命令(names):输出包含三个部分:1 符号值.默认显示十六进制,也可以指定: 2 符号类型.小写表示是本地符号,大写表示全局符号(external); 3 符号名称. 例如:nm Simple ...

  10. 差分进化算法介绍及matlab实现

    引言 差分进化算法是基于群体智能理论的优化算法,是通过群体内个体间的合作与竞争而产生的智能优化搜索算法,它保留了基于种群的全局搜索策略,采用实数编码.基于差分的简单变异操作和"一对一&quo ...