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

入口:ConsumeMessageConcurrentlyService#sendMessageBack

命令编码:RequestCode.CONSUMER_SEND_MSG_BACK;

MQClientAPIImpl#consumerSendMessageBack:

public void consumerSendMessageBack(
final String addr,
final MessageExt msg,
final String consumerGroup,
final int delayLevel,
final long timeoutMillis,
final int maxConsumeRetryTimes
) throws RemotingException, MQBrokerException, InterruptedException {
ConsumerSendMsgBackRequestHeader requestHeader = new ConsumerSendMsgBackRequestHeader();
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.CONSUMER_SEND_MSG_BACK, requestHeader); requestHeader.setGroup(consumerGroup);
requestHeader.setOriginTopic(msg.getTopic());
requestHeader.setOffset(msg.getCommitLogOffset());
requestHeader.setDelayLevel(delayLevel);
requestHeader.setOriginMsgId(msg.getMsgId());
requestHeader.setMaxReconsumeTimes(maxConsumeRetryTimes); RemotingCommand response = this.remotingClient.invokeSync(MixAll.brokerVIPChannel(this.clientConfig.isVipChannelEnabled(), addr),
request, timeoutMillis);
assert response != null;
switch (response.getCode()) {
case ResponseCode.SUCCESS: {
return;
}
default:
break;
} throw new MQBrokerException(response.getCode(), response.getRemark());
}
public class ConsumerSendMsgBackRequestHeader implements CommandCustomHeader {
@CFNotNull
private Long offset;//消息物理偏移量
@CFNotNull
private String group;//消费组名
@CFNotNull
private Integer delayLevel;//延迟级别,mq不支持精确的定时消息调度,而是提供几个延时级别
private String originMsgId;//消息ID
private String originTopic;//消息主题
@CFNullable
private boolean unitMode = false;
private Integer maxReconsumeTimes;//最大重新消费次数,默认为16秒

服务端处理:

SendMessageProcessor#consumerSendMsgBack

获取消费组的订阅配置信息,如果配置信息为空返回配置组信息不存在

public class SubscriptionGroupConfig {

    private String groupName;//消费组名

    private boolean consumeEnable = true;//是否可以消费
private boolean consumeFromMinEnable = true;//是否允许从队列最小偏移量开始消费 private boolean consumeBroadcastEnable = true;//能否以广播模式消费,若为false只能以集群模式消费 private int retryQueueNums = 1;//重试队列个数,默认为1,每一个Broker上一个重试队列 private int retryMaxTimes = 16;消息最大重试次数 private long brokerId = MixAll.MASTER_ID;//masterId private long whichBrokerWhenConsumeSlowly = 1;//如果消息堵塞,将转向改brokerId的服务器上拉取消息默认1 private boolean notifyConsumerIdsChangedEnable = true;//当消息发送变化时是否立即进行消息队列重新负载。

消费组订阅信息配置信息存储在Broker的${ROCKET_HOME}/store/config/subscription/Group.json。默认情况下BrokerConfig.autoCreateSubscriptionGroup默认为true,表示在第一次使用消费组配置信息时如果不存在,则使用上述默认值自动创建一个,如果为false,则只能通过客户端命令mqadmin updateSubGrouop创建后修改相关参数。

创建重试主题,重试主题名称:%RETRY%+消费组名称,并从重试队列汇总随机选择一个队列,并构建TopicConfig主题配置信息。

根据消息物理偏移量从commitlog文件中获取消息,同时将消息的主题存入属性中。

设置消息重试次数,如果消息已重试次数超过maxReconsumeTimes,再次改变newTopic主体为DLQ(%DLQ%),该主题的权限为只写,说明消息一旦进入到DLQ队列中,RocketMQ将不负责再次调度进行消费了需要人工干预。

根据原先的消息创建一个新的消息对象,重试消息会拥有自己的唯一消息ID并存入commitlog文件中,并不会去更新原先消息,而是会将原先的主题,消息ID存入消息的属性中,主题名称为重试主题,其他属性与原先消息保持相同。

消息持久化。

这里再重新回顾一下delayTimeLevel>0的情况:

在存入commitlog文件之前,如果消息的延迟级别delayTimeLevel大于0,替换消息的主题与队列为定时任务主题"SCHEDULE_TOPIC_XXXX",队列ID为延迟级别减1。再次将消息主题,队列存入消息的属性中。

ack消息存入CommitLog文件后,将依托RocketMQ定时消息机制在延迟时间到期后再次将消息拉取,提交消费线程池。ACK消息是同步发送的,如果在发送过程中出现错误,将记录所有发送ACK消息失败的消息,然后再次封装成ConsumeRequest,延迟5s执行。

【mq读书笔记】消息确认(失败消息,定时队列重新消费)的更多相关文章

  1. 【mq读书笔记】客户端处理消息(回调提交到异步业务线程池,pullRequest重新入队)

    看一下客户端收到消息后的处理: MQClientAPIImpl#processPullResponse private PullResult processPullResponse( final Re ...

  2. Rabbitmq之高级特性——百分百投递消息&消息确认模式&消息返回模式实现

    rabbitmq的高级特性: 如何保障消息的百分之百成功? 要满足4个条件:生产方发送出去,消费方接受到消息,发送方接收到消费者的确认信息,完善的消费补偿机制 解决方案,1)消息落库,进行消息状态打标 ...

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

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

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

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

  5. 【mq读书笔记】mq事务消息

    关于mq食物以什么样的方式解决了什么样的问题可以参考这里: https://www.jianshu.com/p/cc5c10221aa1 上文中示例基于mq版本较低较新的版本中TransactionL ...

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

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

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

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

  8. 【mq读书笔记】消息消费队列和索引文件的更新

    ConsumeQueue,IndexFile需要及时更新,否则无法及时被消费,根据消息属性查找消息也会出现较大延迟. mq通过开启一个线程ReputMessageService来准时转发commitL ...

  9. 【mq读书笔记】消息队列负载与重新分配(分配 新队列pullRequest入队)

    回顾PullMessageService#run: 如果队列总没有PullRequest对象,线程将阻塞. 围绕PullRequest有2个问题: 1.PullRequest对象在什么时候创建并加入p ...

随机推荐

  1. Luogu P3757 [CQOI2017]老C的键盘

    题目描述 老C的键盘 题解 显然对于每个数 x 都有唯一对应的 \(x/2\) , 然而对于每个数 x 却可以成为 \(x*2\) 和 \(x*2+1\) 的对应数 根据这一特性想到了啥??? 感谢l ...

  2. JWT原理

    1.COOKIE使用和优缺点 https://www.cnblogs.com/xiaonq/p/11094480.html   1.1 cookie原理: 用户名+密码 cookie是保存在用户浏览器 ...

  3. Unity报错:xxx AnimationEvent has no function name specified!

    参考:https://blog.csdn.net/register_man/article/details/54172778 在开发时出现了题目中的错误且有动画掉帧的情况,搜索后发现是在动画编辑器中我 ...

  4. Java线程池原理及分析

    线程池是很常用的并发框架,几乎所有需要异步和并发处理任务的程序都可用到线程池. 使用线程池的好处如下: 降低资源消耗:可重复利用已创建的线程池,降低创建和销毁带来的消耗: 提高响应速度:任务到达时,可 ...

  5. JavaScript之构造函数

    在学习构造函数之前我们需要知道我们学习构造函数需要学习什么: 1.什么是构造函数  2.构造函数用来做什么 3.构造函数的执行过程  4.构造函数的返回值 1.所以首先我们需要知道什么是构造函数: 在 ...

  6. 【SpringBoot】04.SpringBoot整合Filter的两种方式

    SpringBoot整合Filter过滤器的两种方式: 1.通过扫描注解完成Filter组件注册 创建一个类,实现Filter接口,实现doFilter()方法 在该类使用注解@WebFilter,设 ...

  7. 面试题:能谈谈Date、Datetime、Time、Timestamp、year的区别吗?

    一. 推荐阅读 首发地址:https://mp.weixin.qq.com/s/9zKX86P4kzlKla6-NyS3EA 使用推荐阅读,有更好的阅读体验 二.准备 如果面试官问你:了解 date. ...

  8. 守护进程详解以及start-stop-daemon命令

    1.概念:守护进程是在后台运行的不受终端控制的进程,通常守护进程在系统启动时自动运行,守护进程的名称通常以d结尾,比如sshd.xinetd.crond等. 2.创建守护进程的步骤:a.调用fork( ...

  9. 《GNU_Makefile》第4章——makefile规则

    规则明确在什么情况下,使用什么方法,重构文件,该文件称为目标. make的唯一目的是重构终极目标.终极目标默认是第一个目标. 1. 2.规则语法 TARGETS : PREREQUISITES COM ...

  10. 记录一个处理Excel的新插件:Alibaba Easy Excel

    EasyExcel是一个基于Java的简单.省内存的读写Excel的开源项目.在尽可能节约内存的情况下支持读写百M的Excel.github地址:https://github.com/alibaba/ ...