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

接上文的集群模式,监听器返回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 

【mq读书笔记】消息确认(失败消息,定时队列重新消费)_第1张图片

 

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

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创建后修改相关参数。

 

【mq读书笔记】消息确认(失败消息,定时队列重新消费)_第2张图片

 

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

 

 

【mq读书笔记】消息确认(失败消息,定时队列重新消费)_第3张图片

 

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

 

 

【mq读书笔记】消息确认(失败消息,定时队列重新消费)_第4张图片

 

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

【mq读书笔记】消息确认(失败消息,定时队列重新消费)_第5张图片

 

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

 

 

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

 

消息持久化。

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

 

 

【mq读书笔记】消息确认(失败消息,定时队列重新消费)_第7张图片

 

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

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

你可能感兴趣的:(【mq读书笔记】消息确认(失败消息,定时队列重新消费))