重复消费(幂等性)

重复消费场景

1 consumer 返回失败

public enum ConsumeConcurrentlyStatus {
    CONSUME_SUCCESS,
    RECONSUME_LATER;
}

如果返回 ConsumeConcurrentlyStatus.RECONSUME_LATER,消息就会重试,就会重复消费

2 consumer 消费超时

模式 重试次数 重试间隔 队列
并发 16(默认) 10s, 30s, 1m, 2m, 3m, 4m, 5m, 6m, 7m, 8m, 9m, 10m, 20m, 30m, 1h, 2h 重试队列(%RETRY%消费者组名称
顺序 无限制(int 最大值) 1000ms 原始队列

消费者还在处理消息中,还没有结束,但是达到了重试间隔,所以消息消费会进行重试,如果是并发模式,可以通过 getReconsumeTimes() 获取到重试次数

3 producer 多次发送

生产者也可能发消息超时(进行发送重试),网络波动等原因,消息都重复发送了,消费时也就会重复了

4 广播模式

消息分发是有集群和广播两种模式,如果是广播模式,Broker 会把同一条消息投递给订阅了想通的多个消费者组,这时消费就会重复(正常情况)

消息幂等处理

消息重复消费的几率还是比较大的,在广播模式下本来就要进行重复消费(如果有多个消费者组订阅了同一个 Topic 的情况下),所以要做好消费幂等

通过唯一键比如消息ID,比如唯一业务标识等,每个消费者消费消息时,检查消息是否已被消费,可以使用 redis、事务表等方式实现

// redis 消息幂等处理示例
consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
    String orderId = msgs[0].getUserProperty("orderId");
    // 使用Redis原子操作判断是否已处理
    Boolean isNew = redisTemplate.opsForValue().setIfAbsent("order:" + orderId, "1", 24, TimeUnit.HOURS);
    if (Boolean.TRUE.equals(isNew)) {
        // 首次处理
        orderService.processOrder(orderId);
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    } else {
        // 已处理过,直接确认
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; 
    }
});
posted @ 2025-07-05 09:54  CyrusHuang  阅读(16)  评论(0)    收藏  举报