java 怎么保证RabbitMq消息不丢失

在 Java 中使用 RabbitMQ 时,要保证消息不丢失,需要从消息生产者、RabbitMQ 服务器、消息消费者三个方面进行处理,以下为你详细介绍每个环节的实现方式。

生产者端

1. 开启事务机制

  • 原理:和数据库事务类似,生产者开启事务后,若消息未正确到达 RabbitMQ 服务器,可进行回滚操作。不过该机制会阻塞线程,影响性能。
  • 示例代码
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class TransactionProducer {
    private static final String QUEUE_NAME = "test_queue";

    public static void main(String[] args) {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            // 声明队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            // 开启事务
            channel.txSelect();
            try {
                String message = "Hello, RabbitMQ!";
                // 发送消息
                channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
                // 提交事务
                channel.txCommit();
                System.out.println("消息发送成功");
            } catch (IOException e) {
                // 回滚事务
                channel.txRollback();
                System.out.println("消息发送失败,已回滚");
            }
        } catch (IOException | TimeoutException e) {
            e.printStackTrace();
        }
    }
}

2. 使用发布确认机制

  • 原理:这是一种异步确认方式,生产者发送消息后,RabbitMQ 会返回确认消息,生产者据此判断消息是否发送成功,对性能影响较小。
  • 示例代码
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmCallback;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class ConfirmProducer {
    private static final String QUEUE_NAME = "test_queue";

    public static void main(String[] args) {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            // 声明队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            // 开启发布确认
            channel.confirmSelect();

            String message = "Hello, RabbitMQ!";
            // 发送消息
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());

            // 处理确认回调
            ConfirmCallback ackCallback = (deliveryTag, multiple) -> {
                System.out.println("消息发送成功,deliveryTag: " + deliveryTag);
            };
            ConfirmCallback nackCallback = (deliveryTag, multiple) -> {
                System.out.println("消息发送失败,deliveryTag: " + deliveryTag);
            };
            channel.addConfirmListener(ackCallback, nackCallback);

            // 等待确认
            if (!channel.waitForConfirms()) {
                System.out.println("消息发送失败");
            }
        } catch (IOException | TimeoutException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

RabbitMQ 服务器端

1. 持久化队列和消息

  • 原理:将队列和消息设置为持久化,可确保 RabbitMQ 服务器重启时队列和消息不丢失。
  • 示例代码
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class PersistentProducer {
    private static final String QUEUE_NAME = "test_queue";

    public static void main(String[] args) {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            // 声明持久化队列
            boolean durable = true;
            channel.queueDeclare(QUEUE_NAME, durable, false, false, null);

            String message = "Hello, RabbitMQ!";
            // 发送持久化消息
            channel.basicPublish("", QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
            System.out.println("消息发送成功");
        } catch (IOException | TimeoutException e) {
            e.printStackTrace();
        }
    }
}

消费者端

手动确认消息

  • 原理:消费者接收到消息后,处理完再手动发送确认消息给服务器。若处理异常未确认,服务器会重新投递消息。
  • 示例代码
import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class ManualAckConsumer {
    private static final String QUEUE_NAME = "test_queue";

    public static void main(String[] args) {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            // 声明队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            System.out.println("等待接收消息...");

            // 关闭自动确认
            boolean autoAck = false;
            channel.basicConsume(QUEUE_NAME, autoAck, new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    String message = new String(body, "UTF-8");
                    try {
                        // 处理消息
                        System.out.println("接收到消息: " + message);
                        // 手动确认消息
                        channel.basicAck(envelope.getDeliveryTag(), false);
                    } catch (Exception e) {
                        System.out.println("消息处理失败: " + e.getMessage());
                        // 可以选择拒绝消息并重新入队
                        channel.basicNack(envelope.getDeliveryTag(), false, true);
                    }
                }
            });
        } catch (IOException | TimeoutException e) {
            e.printStackTrace();
        }
    }
}

通过以上在生产者、服务器和消费者端的处理,能够在 Java 中最大程度保证 RabbitMQ 消息不丢失。

posted @ 2025-03-03 10:21  皇问天  阅读(108)  评论(0)    收藏  举报