06-RabbitMQ消息丢失

MQ消息丢失

  • 消息丢失可能出现在三个场景:生产者发送消息时,消息中间件丢失消息,消费者消息丢失

生产者消息发送消息丢失

产生的原因:消息发出后,网路问题,消息中间件没有收到信息

解决方案:

开启RabbitMQ的事务机制

  try {
      channel.txSelect();
      // 发送消息
      // String exchange, String routingKey, BasicProperties props, byte[] body
      channel.basicPublish("", QUEUE_NAME, null, (msg).getBytes());
      // int i =1/0;
      channel.txCommit();
      System.out.println("消息发送成功");
  } catch (Exception e) {
      channel.txRollback(); // 消息回滚
      System.out.println("消息已经回滚");
  }

开启Confirm模式

  • Confirm模式有三种模式:普通确认模式,异步确认模式,批量确认模式

普通确认模式

  • 普通Confirm,发送一条,确认一条
  • 不足:效率较低
  // 开启发送方确认模式
  channel.confirmSelect();
  channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
  // 普通Confirm,发送一条,确认一条
  if (channel.waitForConfirms()) {
      System.out.println("消息发送成功" );
  }

批量确认模式

  • 批量发送信息
  • 不足:批次发送的数量不确定,如果批量发送中有一条数据出现异常,所有消息要进行补偿重发
 try {
    channel.confirmSelect();
    for (int i = 0; i < 5; i++) {
        // 发送消息
        // String exchange, String routingKey, BasicProperties props, byte[] body
        channel.basicPublish("", QUEUE_NAME, null, (msg +"-"+ i).getBytes());
    }
    // 批量确认结果,ACK如果是Multiple=True,代表ACK里面的Delivery-Tag之前的消息都被确认了
    // 比如5条消息可能只收到1个ACK,也可能收到2个(抓包才看得到)
    // 直到所有信息都发布,只要有一个未被Broker确认就会IOException
    channel.waitForConfirmsOrDie();
    System.out.println("消息发送完毕,批量确认成功");
} catch (Exception e) {
    // 发生异常,可能需要对所有消息进行重发
    e.printStackTrace();
}

异步确认模式

  • 一边发送以边确认
   // 用来维护未确认消息的deliveryTag
   final SortedSet<Long> confirmSet = Collections.synchronizedSortedSet(new TreeSet<Long>());

    // 这里不会打印所有响应的ACK;ACK可能有多个,有可能一次确认多条,也有可能一次确认一条
    // 异步监听确认和未确认的消息
    // 如果要重复运行,先停掉之前的生产者,清空队列
    channel.addConfirmListener(new ConfirmListener() {
        public void handleNack(long deliveryTag, boolean multiple) throws IOException {
            System.out.println("Broker未确认消息,标识:" + deliveryTag);
            if (multiple) {
                // headSet表示后面参数之前的所有元素,全部删除
                confirmSet.headSet(deliveryTag + 1L).clear();
            } else {
                confirmSet.remove(deliveryTag);
            }
            // 这里添加重发的方法
        }
        public void handleAck(long deliveryTag, boolean multiple) throws IOException {
            // 如果true表示批量执行了deliveryTag这个值以前(小于deliveryTag的)的所有消息,如果为false的话表示单条确认
            System.out.println(String.format("Broker已确认消息,标识:%d,多个消息:%b", deliveryTag, multiple));
            if (multiple) {
                // headSet表示后面参数之前的所有元素,全部删除
                confirmSet.headSet(deliveryTag + 1L).clear();
            } else {
                // 只移除一个元素
                confirmSet.remove(deliveryTag);
            }
            System.out.println("未确认的消息:"+confirmSet);
        }
    });

    // 开启发送方确认模式
    channel.confirmSelect();
    for (int i = 0; i < 10; i++) {
        long nextSeqNo = channel.getNextPublishSeqNo();
        // 发送消息
        // String exchange, String routingKey, BasicProperties props, byte[] body
        channel.basicPublish("", QUEUE_NAME, null, (msg +"-"+ i).getBytes());
        confirmSet.add(nextSeqNo);
    }
    System.out.println("所有消息:"+confirmSet);

事务模式和Confirm模式的比较

  • 性能方面:事务模式是同步进行,流程会阻塞,吞吐率会下降,比较损耗性能;confirm模式正好相反,异步执行,因此不会阻塞流程;

消息中间件发送消息过程中消息丢失

产生的原因:RabbitMQ接收到信息后出现故障,并且消息还没来得及发送给消费者,且没有进行持久化

解决方案:

  • 创建队列时设置队列持久化
@Bean("GpQueue")
public Queue GpQueue() {
  // queueName, durable, exclusive, autoDelete, Properties
  return new Queue("GP_TEST_QUEUE", true, false, false, new HashMap<>());
}
  • 配置消息的持久化
@Bean("GpExchange")
public DirectExchange exchange() {
  // exchangeName, durable, exclusive, autoDelete, Properties
  return new DirectExchange("GP_TEST_EXCHANGE", true, false, new HashMap<>());
}

消费者接收消息和处理消息过程中消息丢失

产生的原因:消费者信息处理失败的情况下,触发了自动ACK提交,出现了数据未消费成功却返回ack信息

解决方案:

posted @   PerfectLi  阅读(73)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
历史上的今天:
2020-08-17 vue-router.esm.js: Error: "Loading chunk 0 failed"
点击右上角即可分享
微信分享提示