• 6.RokcketMQ消息重试与死信队列


    消息发送重试机制

    Producer对发送失败的消息进行重新发送的机制,称为消息发送重试机制,也称为消息重投机制。

    对于消息重投,需要注意以下几点:

    • 生产者在发送消息时,若采用同步或异步发送方式,发送失败会重试,但oneway消息发送方式发送失败是没有重试机制的
    • 只有普通消息具有发送重试机制,顺序消息是没有的
    • 消息重投机制可以保证消息尽可能发送成功、不丢失,但可能会造成消息重复。消息重复在RocketMQ中是无法避免的问题
    • 消息重复在一般情况下不会发生,当出现消息量大、网络抖动,消息重复就会成为大概率事件
    • producer主动重发、consumer负载变化(发生Rebalance,不会导致消息重复,但可能出现重复消费)也会导致重复消息
    • 消息重复无法避免,但要避免消息的重复消费。
    • 避免消息重复消费的解决方案是,为消息添加唯一标识(例如消息key),使消费者对消息进行消费判断来避免重复消费
    • 消息发送重试有三种策略可以选择:同步发送失败策略、异步发送失败策略、消息刷盘失败策略

    同步发送失败策略

    // 创建一个producer,参数为Producer Group名称
    DefaultMQProducer producer = new DefaultMQProducer("pg");
    // 指定nameServer地址
    producer.setNamesrvAddr("node1:9876");
    // 设置同步发送失败时重试发送的次数,默认为 2 次
    producer.setRetryTimesWhenSendFailed( 3 );
    // 设置发送超时时限为5s,默认3s
    producer.setSendMsgTimeout( 5000 );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    同时,Broker还具有失败隔离功能,使Producer尽量选择未发生过发送失败的Broker作为目标Broker。其可以保证其它消息尽量不发送到问题Broker,为了提升消息发送效率,降低消息发送耗时

    异步发送失败策略

    DefaultMQProducer producer = new DefaultMQProducer("pg");
    producer.setNamesrvAddr("node1:9876");
    // 指定异步发送失败后不进行重试发送
    producer.setRetryTimesWhenSendAsyncFailed( 0 );
    
    • 1
    • 2
    • 3
    • 4

    消息刷盘失败策略

    消息刷盘超时(Master或Slave)或slave不可用(slave在做数据同步时向master返回状态不是SEND_OK)时,默认是不会将消息尝试发送到其他Broker的。不过,对于重要消息可以通过在Broker的配置文件设置retryAnotherBrokerWhenNotStoreOK属性为true来开启。

    消息重复消费机制

    有序消息重复消费

    对于顺序消息,当Consumer消费消息失败后,为了保证消息的顺序性,其会自动不断地进行消息重试,直到消费成功。消费重试默认间隔时间为 1000 毫秒。重试期间应用会出现消息消费被阻塞的情况。

    DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("cg");
    // 顺序消息消费失败的消费重试时间间隔,单位毫秒,默认为 1000 ,其取值范围为[10,30000]
    consumer.setSuspendCurrentQueueTimeMillis( 100 );
    
    • 1
    • 2
    • 3

    由于对顺序消息的重试是无休止的,不间断的,直至消费成功,所以,对于顺序消息的消费,务必要保证应用能够及时监控并处理消费失败的情况,避免消费被永久性阻塞。

    注意,顺序消息没有发送失败重试机制,但具有消费失败重试机制

    无序消息重复消费

    对于无序消息(普通消息、延时消息、事务消息),当Consumer消费消息失败时,可以通过设置返回状态达到消息重试的效果。不过需要注意,无序消息的重试只对集群消费方式生效,广播消费方式不提供失败重试特性。即对于广播消费,消费失败后,失败消息不再重试,继续消费后续消息。

    消息重试次数与间隔

    对于无序消息集群消费下的重试消费,每条消息默认最多重试 16 次,但每次重试的间隔时间是不同的,会逐渐变长。每次重试的间隔时间如下表。

    重试次数与上次重试的间隔时间重试次数与上次重试的间隔时间
    110秒97分钟
    230108 分钟
    31分钟119 分钟
    42分钟1210分钟
    53分钟1320分钟
    64分钟1430分钟
    75分钟151小时
    86分钟162 小时

    若一条消息在一直消费失败的前提下,将会在正常消费后的第 4 小时 46 分后进行第 16 次重试。
    若仍然失败,则将消息投递到死信队列

    DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("cg");
    // 修改消费重试次数
    consumer.setMaxReconsumeTimes( 10 );
    
    • 1
    • 2
    • 3

    对于修改过的重试次数,将按照以下策略执行:
    1)若修改值小于 16 ,则按照指定间隔进行重试
    2)若修改值大于 16 ,则超过 16 次的重试时间间隔均为 2 小时

    对于Consumer Group,若仅修改了一个Consumer的消费重试次数,则会应用到该Group中所有其它Consumer实例。若出现多个Consumer均做了修改的情况,则采用覆盖方式生效。即最后被修改的值会覆盖前面设置的值。

    重试队列

    对于需要重试消费的消息,并不是Consumer在等待了指定时长后再次去拉取原来的消息进行消费,而是将这些需要重试消费的消息放入到了一个特殊Topic的队列中,而后进行再次消费的。这个特殊的队列就是重试队列。

    当出现需要进行重试消费的消息时,Broker会为每个消费组都设置一个Topic名称为%RETRY%consumerGroup@consumerGroup的重试队列。

    • 这个重试队列是针对消息者组的,而不是针对每个Topic设置的(一个Topic的消息可以让多个消费者组进行消费,所以会为这些消费者组各创建一个重试队列)
    • 只有当出现需要进行重试消费的消息时,才会为该消费者组创建重试队列

    注意,消费重试的时间间隔与延时消费延时等级十分相似,除了没有延时等级的前两个时间外,其它的时间都是相同的

    Broker对于重试消息的处理是通过延时消息实现的。先将消息保存到SCHEDULE_TOPIC_XXXX延迟队列中,延迟时间到后,会将消息投递到%RETRY%consumerGroup@consumerGroup重试队列中。

    消费重试配置方式

    • 方式 1 :返回ConsumeConcurrentlyStatus.RECONSUME_LATER(推荐)
    • 方式 2 :返回Null
    • 方式 3 :抛出异常

    集群消费方式下,消息消费失败后若不希望消费重试,则在捕获到异常后同样也返回与消费成功后的相同的结果,即ConsumeConcurrentlyStatus.CONSUME_SUCCESS,则不进行消费重试。

    死信队列

    当一条消息初次消费失败,消息队列会自动进行消费重试;达到最大重试次数后,若消费依然失败,则表明消费者在正常情况下无法正确地消费该消息,此时,消息队列不会立刻将消息丢弃,而是将其发送到该消费者对应的特殊队列中。这个队列就是死信队列(Dead-Letter Queue,DLQ),而其中的消息 则称为死信消息(Dead-Letter Message,DLM)

    死信队列是用于处理无法被正常消费的消息的

    死信队列特征

    • 死信队列中的消息不会再被消费者正常消费,即DLQ对于消费者是不可见的
    • 死信存储有效期与正常消息相同,均为 3 天(commitlog文件的过期时间), 3 天后会被自动删除
    • 死信队列就是一个特殊的Topic,名称为%DLQ%consumerGroup@consumerGroup,即每个消费者组都有一个死信队列
    • 如果一个消费者组未产生死信消息,则不会为其创建相应的死信队列

    死信队列处理

    实际上,当一条消息进入死信队列,就意味着系统中某些地方出现了问题,从而导致消费者无法正常消费该消息,比如代码中原本就存在Bug。因此,对于死信消息,通常需要开发人员进行特殊处理。最关键的步骤是要排查可疑因素,解决代码中可能存在的Bug,然后再将原来的死信消息再次进行投递消费。

  • 相关阅读:
    etcd使用与原理【22Fa】
    实验四.路由器静态路由的配置
    17.2 实现无管道正向CMD
    AMD发布22.8.2驱动,支持《黑道圣徒·重制版》
    Java应用工程结构
    一、RocketMQ安装
    Python绘图系统24:绘图类型和坐标映射的关系
    数据库及程序日常开发命名实践【四期】
    数据结构 —— BellmanFord算法
    reticulate | R-python调用 | 安装及配置 | conda文件配置
  • 原文地址:https://blog.csdn.net/woshiwjma956/article/details/126040314