Rocketmq 重复消息,幂等

一、为什么出现消息重复

从 Product 看

Rocketmq 提供三种发送消息模式

同步发送:Producer 向 broker 发送消息,阻塞当前线程等待 broker 响应 发送结果。DefaultMQProducerImpl 中如果没有设置 超时、发送失败,就会重发。

异步发送:先构建一个broker发送消息的任务,把任务提交给线程池,等执行完任务时,回调用户自定义的回调函数,执行处理结果。

Oneway发送:只负责发送请求,不等待应答,

 

注:同步发送、异步发送 如果发送成功,返回结果出现网络问题,会导致重新发送,多条重复消息。

从 Consumer 看

Broker 消息进度丢失,导致消息重复投递给 Consumer。

Consumer 消费成功,但是因为网络问题,JVM 异常崩溃,导致rocketmq没收到 消费成功确认,会重复推送。

注:从性能考虑,消费进度 用异步定时同步给 Broker。

 

 

 

二、Rocketmq ack 机制保证消息消费成功。

ACK

发送者为保证消息肯定消费成功,需要使用方明确标识消费成功,rocketmq 才会认为消息消费成功。中途断电,抛出异常等都不会认为成功(会重新投递)。

public enum Action {
    /**
     * 消费成功,继续消费下一条消息
     */
    CommitMessage,
    /**
     * 消费失败,告知服务器稍后再投递这条消息,继续消费其他消息
     */
    ReconsumeLater,
}

例:

消费者回执


import com.aliyun.openservices.ons.api.Action;
import com.aliyun.openservices.ons.api.ConsumeContext;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.MessageListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RuleForwardListener implements MessageListener {
    private Logger log = LoggerFactory.getLogger(RuleForwardListener.class);

    @Override
    public Action consume(Message message, ConsumeContext context) {
        try {
            String msg = new String(message.getBody());
            log.info("rrpc response message:{}", msg);
            return Action.CommitMessage;  // 消费成功
        } catch (Throwable t) {
            log.error("rrpc-response error", t);
            return Action.ReconsumeLater;  // 消费失败
        }
    }
}

 

仅当回执函数返回 CommitMessage时,rocketmq 就会认为此消息消费成功。

返回 ReconsumeLater ,rocketmq 认为消息消费失败。

 

为保证消息肯定被至少消费成功一次,rocketmq 会把消息重发回 broker(topic不是原topic 而是消费组的 retry topic),在延迟的某个时间点(默认 10s, 可设置),再次投递到这个 ConsumerGroup。如果一直这样重复消费都失败,默认 16次,就会投递到 DLQ 死信队列。应用可以监控死信队列来做人工干预。

 

修改重试时间

broker 日志中发现默认重试时间:

messageDelayLevel = 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

 

可以在配置文件中设置:

messageDelayLevel = 10s 1m 5m

(或者在逻辑中,人工干预重复次数。达到次数后,返回 CommitMessage )

 

三、解决重复消费 - 消费者实现 消费幂等。

根据业务上 唯一 key 对消息做幂等处理。

当出现消费者对某条消息重复消费的情况时,重复消费的结果与消费一次的结果相同,并且多次消费并未对业务系统产生任何负面影响,那么整个过程就可实现消息幂等

Message 中的 key

Message message = new Message(msgTopic, msgTag, "uuid", messageBody);
SendResult sendResult = producer.send(message);

 

 

 

posted @ 2021-02-09 17:59  currentTimeMillis  阅读(985)  评论(0编辑  收藏  举报