How does RabbitMQ ensure that messages are not lost?

Get into the habit of writing! This is the third day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event .

1. Background

In the interview process, this question is a must answer. The blogger has organized it in many ways and summarized the following. If there is an interview requirement, you can recite it appropriately.

2. The process of message delivery

Let’s first explain a concept, what is reliable delivery? In RabbitMQ, a message sent from the producer to the RabbitMQ server needs to go through the following steps:insert image description here

  1. The producer is ready to deliver the message.
  2. The producer establishes a connection with the RabbitMQ server.
  3. The producer sends the message.
  4. The RabbitMQ server receives the message and routes it to the specified queue.
  5. The RabbitMQ server initiates a callback to inform the producer that the message was sent successfully.

The so-called reliable delivery is to ensure that 100% of the message can be sent from the producer to the server.

3. Solutions

The following problems exist in the queue: message loss problem, repeated consumption problem, the following is the solution point

1. Queue persistent hard disk

The lost process only loses the message when the memory is sent to the disk. If it is saved to the disk, the restart service message will not be lost, but it will affect the efficiency.

new Queue("demo_queue", true, false, false, args); 第二个参数为true
复制代码

2. Manual ack

Tell the producer that the message succeeded/failed, otherwise, if it fails the queue will remain suspended and their message will wait. So after the consumption is completed, the producer is notified whether the consumption is successful/failed, which is implemented using ack/nack. Mainly through the configuration file to achieve.

  rabbitmq:
    host: 192.168.xx.xx
    port: 5672
    username: root
    password: root
    virtual-host: /
    listener:
      simple:
        acknowledge-mode: manual  #手动应答
        prefetch: 1 # 每次只处理一个信息
    publisher-confirms: true #开启消息确认机制
    publisher-returns: true #支持消息发送失败返回队列
复制代码
    @RabbitListener(queues = "demo_queue")
    protected void consumerDead(Message message, Channel channel) throws Exception {
        RabbitEnum ackSign = RabbitEnum.ACCEPT;
        try {
            int i = 10 / 0;
        } catch (Exception e) {
            ackSign = RabbitEnum.RETRY;
            throw e;
        } finally {
            // 通过finally块来保证Ack/Nack会且只会执行一次
            if (ackSign == RabbitEnum.ACCEPT) {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            } else if (ackSign == RabbitEnum.RETRY) {
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
            }
        }
    }
复制代码

3. Confirm whether the sending is successful

Determine if the message is sent to the switch.

 @Bean
    public RabbitTemplate rabbitTemplate() {
        Logger logger = LoggerFactory.getLogger(RabbitTemplate.class);
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        // 消息发送失败返回到队列中, yml需要配置 publisher-returns: true
        rabbitTemplate.setMandatory(true);
        // 发送消息确认, yml需要配置 publisher-confirms: true 消息是否发送的核心配置
        rabbitTemplate.setConfirmCallback(msgSendConfirmCallBack());
        // 消息返回, yml需要配置 publisher-returns: true 消息返回的配置
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            String correlationId = message.getMessageProperties().getCorrelationId().toString();
            logger.debug("消息:{} 发送失败, 应答码:{} 原因:{} 交换机: {}  路由键: {}", correlationId, replyCode, replyText, exchange,
                routingKey);
        });
        return rabbitTemplate;
    }

    /**
     * 确认发送消息是否成功
     *
     * @return
     */
    @Bean
    public MsgSendConfirmCallBack msgSendConfirmCallBack() {
        return new MsgSendConfirmCallBack();
    }
复制代码
public class MsgSendConfirmCallBack implements RabbitTemplate.ConfirmCallback {

    /**
     * 回调方法
     * @param correlationData
     * @param ack
     * @param cause
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        System.out.println("MsgSendConfirmCallBack  , 回调id:" + correlationData);
        if (ack) {
            System.out.println("消息发送成功");
        } else {
            //可以将消息写入本地,使用定时任务重新发送
            System.out.println("消息发送失败:" + cause + "\n重新发送");
        }
    }

}
复制代码

4. Cluster processing

Needless to say, the production environment cluster is standard.

5. Disaster recovery in different places

6. Endurance

Sending messages persists to db for message resending. The consumer message is fixed to the db, and the message id is used to determine whether it is repeated consumption.

Guess you like

Origin juejin.im/post/7082381667993649160