系列十五(面试)、RocketMQ消息重复消费问题

一、RocketMQ消息重复消费问题

1.1、官网

1.2、消息重复被消费原因

通过上述官网的描述我们可以知道,RocketMQ中的消息是存在重复消费的情况的。那么消息为什么会被重复消费呢?先来回顾一下RocketMQ的消息是怎么发送和接收的:

从上图可以看出,导致消息被重复消费的原因有2个:

(1)生产者重复投递消息;

(2)消费者扩容reBalance(重平衡);

1.3、如何解决

1.3.1、概述

在了解了消息被重复消费的原因后,解决起来思路就比较清晰了。这里给出一种比较常见的解决方案:生产者发送消息时给消息带一个唯一标识(例如订单系统,可以将订单号作为唯一标识),消费者消费消息时要控制消息的幂等性。

1.3.2、幂等性

所谓幂等性,通俗的讲就是多次对某条数据的操作产生的影响和第一次操作产生的影响相同。举个MySQL中增删改查的例子方便大家伙理解:例如数据库中存在一个account表,它有如下字段:id name balance;那么执行增删改时,幂等性情况如下:

1.3.3、解决方法(思路)

(1)发送者发送消息时,需要给消息携带一个唯一表示(自己业务控制);

(2)消费者收到消息后,要控制消息的幂等性;

1.3.4、案例

java 复制代码
/**
 * 测试消息重复消费:生产者端发送两条key相同的消息
 */
@Test
public void sendRepeatableMessage() {
	String key = LocalDateTimeUtil.format(LocalDateTime.now(), "yyyyMMddHHmmssSSS") + new Random().nextInt(5);
	log.info("key:{}", key);
	Message<String> keyMessage = MessageBuilder
			.withPayload("boot 这是一个Key为【" + key + "】的消息")
			.setHeader(RocketMQHeaders.KEYS, key)
			.build();
	Message<String> repeatKeyMessage = MessageBuilder
			.withPayload("boot 这是一个Key为【" + key + "】的消息")
			.setHeader(RocketMQHeaders.KEYS, key)
			.build();
	SendResult keyResult = rocketMQTemplate.syncSend(boot-repeatable-key-topic, keyMessage);
	SendResult repeatKeyResult = rocketMQTemplate.syncSend(boot-repeatable-key-topic, keyMessage);

	log.info("重复消息发送成功,【keyResult sendStatus】:{},【repeatKeyResult sendStatus】:{}",keyResult.getSendStatus(),repeatKeyResult.getSendStatus());
}
java 复制代码
/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/12/26 14:46
 * @Description:
 */
@RocketMQMessageListener(
        topic = "boot-repeatable-key-topic",
        consumerGroup = "boot-repeatable-key-consumer-group"
)
@Slf4j
@Component
public class MyRocketMqRepeatableListener implements RocketMQListener<MessageExt> {

    @Autowired
    private OrderMqLogMapper orderMqLogMapper;

    /**
     * 解决消息重复消费问题
     * @param messageExt
     */
    @Override
    public void onMessage(MessageExt messageExt) {
        String orderSn = messageExt.getKeys();
        String message = StrUtil.utf8Str(messageExt.getBody());
        OrderMqLogDO orderMqLogDO = new OrderMqLogDO().setOrderSn(orderSn).setMessage(message);
        try {
            orderMqLogMapper.insert(orderMqLogDO);
            log.info("doBusiness...,orderSn:{},消息内容:{}",orderSn,message);
        } catch (Exception e) {
            log.error("消息已消费,请勿重新消费! orderSn:{},消息内容:{}",orderSn,message);
            e.printStackTrace();
        }
    }
}
java 复制代码
/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/12/26 14:18
 * @Description: 订单消息消费表,防止重复消费
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@TableName("order_mq_log")
public class OrderMqLogDO implements Serializable {

    /**
     * 主键
     */
    private Long id;

    /**
     * 订单编号
     */
    private String orderSn;

    /**
     * 消息内容
     */
    private String message;

}
相关推荐
ym hyd 1113 小时前
试题库管理系统源码 Java+SpringBoot+Vue3 前后分离
java·vue.js·spring boot·毕设
AC赳赳老秦3 小时前
采集行为合规自检:OpenClaw 自动校验 robots 协议与采集频率,规避违规采集风险
java·开发语言·c++·python·php·deepseek·openclaw
钱栈up3 小时前
mvn compile 卡死在 javac 阶段:一次类型不匹配的排查与修复
java·开发语言
吠品5 小时前
CSS图片模糊过渡:一个transition加filter就够
java·数据库·notepad++
步行cgn5 小时前
Spring 全注解开发详解
java·后端·spring
长河6 小时前
Java的GC(垃圾回收)中,标记无用对象的核心方法是可达性分析算法
java·jvm·算法
code2cat6 小时前
Java进阶篇之LongAdder:把统计更新分散,再读取总和
java·并发编程·原子类·longadder
凯哥Java6 小时前
写代码怎么避免逻辑漏洞?
java·开发语言·人工智能·自动化
Ivanqhz6 小时前
正余弦位置编码(Sinusoidal Positional Encoding)
java·linux·数据库
BD_Marathon7 小时前
消息对象中字段的说明
java·前端·python