消息顺序、消息重复问题

消息顺序、消息重复问题

消息的顺序问题

关键问题暴露点
  1. 消息顺序乱序风险
    • M1(先发)→ S1 → 消费者1
    • M2(后发)→ S2 → 消费者2
    • 若消费者2处理速度 > 消费者1 ,则 M2 结果先于 M1 生效 → 业务状态错乱
    • 典型场景:订单创建(M1)和支付(M2),若先执行支付将报错
  2. 消息重复消费可能
    • 图中虚线通知环节 隐含风险:
      • 通知:M1收到 因网络抖动丢失
      • S1 触发 M1 重发 → 消费者1 重复处理

完整解决方案
消息顺序问题:分区保序方案

M1 订单ID:001 M2 订单ID:001 Hash订单ID %3 Hash订单ID %3 生产者 路由层 队列1 消费者1-单线程消费

技术落地

  • RabbitMQ :使用 一致性Hash交换器 + 单队列单消费者

  • Kafka/RocketMQ :指定 Message Key=订单ID,保证同Key进同一分区

  • 代码示例(Spring Boot + RabbitMQ)

    java 复制代码
    // 发送时绑定订单ID到路由Key
    rabbitTemplate.convertAndSend("order_exchange", "order_001", message);
    
    // 消费者配置:队列绑定单个消费者
    @RabbitListener(queues = "queue_order_001", concurrency = "1")
    public void handleOrder(Message message) { ... }

消息重复问题:三层幂等拦截

技术落地

  • Redis原子锁(防并发重复)

    java 复制代码
    Boolean isDuplicate = redisTemplate.opsForValue()
          .setIfAbsent("msg:"+messageId, "1", 10, TimeUnit.MINUTES);
    if (Boolean.FALSE.equals(isDuplicate)) return;
  • 数据库去重表

    sql 复制代码
    CREATE TABLE msg_uniq (
      id VARCHAR(64) PRIMARY KEY COMMENT '消息ID',
      biz_id VARCHAR(32) COMMENT '业务ID',
      create_time DATETIME DEFAULT CURRENT_TIMESTAMP
    );
  • 业务层乐观锁

    sql 复制代码
    UPDATE orders SET status='paid' 
    WHERE order_id='001' AND status='unpaid'  // 仅状态匹配才更新
消息零丢失方案(增强架构图)
  1. 事务消息 2. 镜像队列 2. 镜像队列 2. 镜像队列 3. 手动ACK 生产者 RabbitMQ集群 S1 Master S2 Slave S3 Slave 消费者

关键配置

  • 生产者端

    java 复制代码
    // RabbitMQ 发送确认
    rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
      if (!ack) log.error("消息未抵达Broker: {}", cause);
    });
  • MQ端

    shell 复制代码
    # RabbitMQ镜像队列策略
    rabbitmqctl set_policy ha-all "^order" '{"ha-mode":"all"}'
  • 消费者端

    java 复制代码
    // 关闭自动ACK
    @RabbitListener(queues = "queue", ackMode = "MANUAL")
    public void handle(Message message, Channel channel) {
      try {
        process(message); // 业务处理
        channel.basicAck(deliveryTag, false); // 手动确认
      } catch (Exception e) {
        channel.basicNack(deliveryTag, false, true); // 重试
      }
    }

生产级架构建议

消费者集群 MQ集群 生产者集群 1. 带Key的消息 Hash Key路由 Hash Key路由 镜像复制 镜像复制 单线程消费 单线程消费 ACK确认 ACK确认 防重:Redis+DB 防重:Redis+DB 消费者1 消费者2 MQ 队列1 队列2 队列1副本 队列2副本 路由层 生产者 分布式锁

各组件职责说明
组件 关键职责
路由层 根据业务Key(如订单ID)哈希选择队列,保证同业务消息顺序性
镜像队列 主从实时同步,任一节点宕机不影响消息可用性(RabbitMQ需部署磁盘节点)
单线程消费 每个队列仅允许一个消费者,避免并发导致顺序混乱(注意:消费者内部用线程池并行会破坏顺序!)
分布式锁 Redis锁控制消费幂等性入口,去重表兜底

相关推荐
Best_Liu~35 分钟前
策略模式 vs 适配器模式
java·spring boot·适配器模式·策略模式
direction__1 小时前
Java Main无法初始化主类的原因与解决方法(VsCode工具)
java·vscode
帧栈1 小时前
开发避坑指南(29):微信昵称特殊字符存储异常修复方案
java·mysql
每天的每一天1 小时前
面试可能问到的问题思考-Redis
java
青云交2 小时前
Java 大视界 -- Java 大数据在智能安防人脸识别系统中的活体检测与防伪技术应用
java·大数据·生成对抗网络·人脸识别·智能安防·防伪技术·活体测试
学习至死qaq2 小时前
信创产品TongLinkQ安装及springboot2整合使用
java·东方通·tonglinkq
我崽不熬夜2 小时前
Java中基本的输入输出(I/O)操作:你知道如何处理文件吗?
java·后端·java ee
我崽不熬夜2 小时前
Java的异常处理机制:如何优雅地捕获和抛出异常?
java·后端·java ee
Slaughter信仰2 小时前
深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)第二章知识点问答(21题)
java·开发语言·jvm
一叶飘零_sweeeet2 小时前
SPI 机制深度剖析:Java、Spring、Dubbo 的服务发现哲学与实战指南
java·spring·dubbo