What is the message acknowledgment mechanism in RabbitMQ? Why do you need message confirmation?

What is the message acknowledgment mechanism in RabbitMQ? Why do you need message confirmation?

The message confirmation mechanism in RabbitMQ refers to a mechanism that waits for the consumer to confirm that the message has been correctly received and processed after the producer sends the message. The main purpose of the message confirmation mechanism is to ensure reliable delivery and processing of messages to avoid message loss or repeated processing.

Why is a message confirmation mechanism needed? In a distributed system, the sending and receiving of messages is an asynchronous process, and the following situations may exist:

  1. Message loss: During the message sending process, the message may be lost due to network failure, hardware failure or other reasons. If there is no message confirmation mechanism, the producer cannot know whether the message is successfully delivered to the consumer, and thus the reliability of the message cannot be guaranteed.

  2. Message duplication: During the message sending process, the message may be sent repeatedly due to network timeout, consumer failure or other reasons. Without a message acknowledgment mechanism, consumers may process the same message multiple times, resulting in repeated operations and data inconsistency.

In order to solve the above problems, RabbitMQ introduced a message confirmation mechanism. The message confirmation mechanism includes two important concepts: Publish Confirm and Consumer Acknowledgement.

Publishing confirmation refers to the process of waiting for the RabbitMQ server to return a confirmation message after the producer sends the message. Producers can channel.confirmSelect()enable publish confirmation mode by calling a method and then use channel.waitForConfirms()the method to wait for a confirmation message to be returned from the server. If the server successfully receives and processes the message, it will return a confirmation message to the producer.

The following is a code example written in Java that demonstrates how to use the message confirmation mechanism:

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.ConfirmListener;

import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.TimeoutException;

public class MessageConfirmationExample {
    
    

    private final static String QUEUE_NAME = "my_queue";
    private final static String MESSAGE = "Hello, RabbitMQ!";

    public static void main(String[] args) throws IOException, TimeoutException {
    
    
        // 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");

        // 创建连接
        Connection connection = factory.newConnection();

        // 创建通道
        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);

        // 启用发布确认模式
        channel.confirmSelect();

        // 创建一个有序集合,用于保存未确认的消息的Delivery Tag
        SortedSet<Long> unconfirmedSet = Collections.synchronizedSortedSet(new TreeSet<>());

        // 添加发布确认监听器
        channel.addConfirmListener(new ConfirmListener() {
    
    
            @Override
            public void handleAck(long deliveryTag, boolean multiple) throws IOException {
    
    
                if (multiple) {
    
    
                    // 多条消息被确认,移除所有小于等于deliveryTag的消息
                    unconfirmedSet.headSet(deliveryTag + 1).clear();
                } else {
    
    
                    // 单条消息被确认,移除该消息
                    unconfirmedSet.remove(deliveryTag);
                }
                System.out.println("Message confirmed: " + deliveryTag);
            }

            @Override
            public void handleNack(long deliveryTag, boolean multiple) throws IOException {
    
    
                if (multiple) {
    
    
                    // 多条消息未被确认,重新发送所有小于等于deliveryTag的消息
                    unconfirmedSet.headSet(deliveryTag + 1).forEach(tag -> {
    
    
                        try {
    
    
                            sendMessage(channel, tag);
                        } catch (IOException e) {
    
    
                            e.printStackTrace();
                        }
                    });
                } else {
    
    
                    // 单条消息未被确认,重新发送该消息
                    sendMessage(channel, deliveryTag);
                }
                System.out.println("Message not confirmed: " + deliveryTag);
            }
        });

        // 发送消息
        for (int i = 1; i <= 10; i++) {
    
    
            long deliveryTag = sendMessage(channel, i);
            unconfirmedSet.add(deliveryTag);
        }

        // 等待所有消息被确认
        try {
    
    
            channel.waitForConfirmsOrDie();
            System.out.println("All messages confirmed!");
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }

        // 关闭通道和连接
        channel.close();
        connection.close();
    }

    private static long sendMessage(Channel channel, int messageNumber) throws IOException {
    
    
        long deliveryTag = channel.getNextPublishSeqNo();
        channel.basicPublish("", QUEUE_NAME, null, (MESSAGE + " " + messageNumber).getBytes());
        System.out.println("Sent message: " + messageNumber);
        return deliveryTag;
    }
}

In the above code, first we create a connection factory and set the host address of the RabbitMQ server. We then created a connection using a connection factory and a channel using the connection. Next, we declare a queue named "my_queue". We then enabled release confirmation mode by calling channel.confirmSelect()the method. At the same time, we created an ordered collection unconfirmedSetto save the Delivery Tag of unconfirmed messages.

Then, we added a release confirmation listener ConfirmListenerand implemented handleAckthe and handleNackmethods in the listener. When a message is acknowledged, handleAckthe method is called and in this method we can handle the logic of the acknowledgment, such as removing the acknowledged message unconfirmedSetfrom it . The method is called when the message is not acknowledged handleNack, and we can handle unacknowledged logic in this method, such as resending unacknowledged messages.

Next, we used sendMessagethe method to send 10 messages and saved the Delivery Tag of each message in unconfirmedSet. We then use channel.waitForConfirmsOrDie()the method to wait for all messages to be acknowledged. If acknowledgments for all messages are not received within the specified time, InterruptedExceptionan exception will be thrown.

Guess you like

Origin blog.csdn.net/qq_51447496/article/details/132892212
Recommended