RabbitMQ Tutorial (iv) how persistent messages guarantee of 99.99% is not lost?

1. antecedent Review

Tutorial RabbitMQ (a) configuration and installation environment RabbitMQ Hello World example

RabbitMQ tutorial (b) RabbitMQ user management, role management and permission settings

RabbitMQ tutorial (c) how to ensure that the message is sent 99.99% success?

In a previous blog, we explain how producers confirmed by RabbitMQ mechanism to ensure the success of the message as much as possible to RabbitMQ server, which only reduces the chances of missing a message from the source mentioned before does not really solve problem: how to ensure that exceptions RabbitMQ (artificial restart, abnormal downtime, etc.), the queues, and the message is not lost?

2. Benpian Summary

To solve this problem, we should use RabbitMQ in the persistence of the concept of the so-called persistent, is the data (Exchange switches, Queue queue, Message message) RabbitMQ will cure in memory to disk, to prevent the occurrence of abnormal conditions, data lost.

Wherein, RabblitMQ persistence divided into three parts:

  1. Switch (Exchange) persistence
  2. Queue (Queue) persistence
  3. Message (Message) persistence

3. Switch (Exchange) persistence

In the blog post, we declare Exchange code like this:

private final static String EXCHANGE_NAME = "normal-confirm-exchange";

// 创建一个Exchange
channel.exchangeDeclare(EXCHANGE_NAME, "direct");

In this case the statement when Exchange is non-persistent, abnormal conditions (restart, downtime) appear in RabbitMQ, the Exchange will be lost, it will affect subsequent write messages to the Exchange , then how to set up Exchange for the persistence of it? The answer is to set up durable parameters.

durable: Set whether persistence. durable set to true, persistent, non-persistent and vice versa.

Persistence switch can save, reboot the server when the relevant information is not lost.

Set Exchange persistence:

channel.exchangeDeclare(EXCHANGE_NAME, "direct", true);

At this overloaded method call is:

public DeclareOk exchangeDeclare(String exchange, String type, boolean durable) throws IOException {
    return this.exchangeDeclare(exchange, (String)type, durable, false, (Map)null);
}

In order to better understand our new production classes are as follows:

package com.zwwhnly.springbootaction.rabbitmq.durable;

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 DurableProducer {
    private final static String EXCHANGE_NAME = "durable-exchange";
    private final static String QUEUE_NAME = "durable-queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 创建连接
        ConnectionFactory factory = new ConnectionFactory();
        // 设置 RabbitMQ 的主机名
        factory.setHost("localhost");
        // 创建一个连接
        Connection connection = factory.newConnection();
        // 创建一个通道
        Channel channel = connection.createChannel();
        // 创建一个Exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

        // 发送消息
        String message = "durable exchange test";
        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());

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

Sample code, we built a non-persistent Exchange, 1 non-persistent Queue, and they did a bind, then run the code, Exchange and New Queue successfully, the message 'durable exchange test' also correctly delivered to the queue:

At this point restart under RabbitMQ service, you will find Exchange lost:

Modifications code parameter to be durable ture:

// 创建一个Exchange
channel.exchangeDeclare(EXCHANGE_NAME, "direct", true);

At this point runs out of code, and then restart the service under RabbitMQ will find Exchange no longer lose:

4. The queue (Queue) persistence

Careful users may find that, although now after the restart RabbitMQ service, Exchange is not lost, but the queues and messages are lost, then how to solve the queue without losing it? The answer is to set the parameters durable.

durable: Set whether persistence. Is set to true queue is persistent.

Persistent queue will be saved, when the server restarts can ensure that relevant information is not lost.

Simple modifications Queue declared above code, the durable parameter to true:

channel.queueDeclare(QUEUE_NAME, true, false, false, null);

At this overloaded method call is as follows:

public com.rabbitmq.client.impl.AMQImpl.Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments) throws IOException {
    validateQueueNameLength(queue);
    return (com.rabbitmq.client.impl.AMQImpl.Queue.DeclareOk)this.exnWrappingRpc((new com.rabbitmq.client.AMQP.Queue.Declare.Builder()).queue(queue).durable(durable).exclusive(exclusive).autoDelete(autoDelete).arguments(arguments).build()).getMethod();
}

Run the code, and then restart the RabbitMQ service, you will find the queue now not lost:

The message (Message) persistence

Although RabbitMQ after the restart, Exchange and Queue are not lost, but the message is stored in the Queue inside but still lost, then how to ensure that messages are not lost it? The answer is to set the message delivery mode 2, which represents persistence.

Modify the code to send a message:

// 发送消息
String message = "durable exchange test";
AMQP.BasicProperties props = new AMQP.BasicProperties().builder().deliveryMode(2).build();
channel.basicPublish(EXCHANGE_NAME, "", props, message.getBytes());

Overloaded method call is:

public void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException {
    this.basicPublish(exchange, routingKey, false, props, body);
}

Run the code, and then restart the RabbitMQ service, found at this time Exchange, Queue, the message is not lost:

At this point, we have the perfect solution to RabbitMQ after the restart, message loss problems.

The final code looks like, you can also download all the source code used in this article through the source link at the end of the text:

package com.zwwhnly.springbootaction.rabbitmq.durable;

import com.rabbitmq.client.AMQP;
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 DurableProducer {
    private final static String EXCHANGE_NAME = "durable-exchange";
    private final static String QUEUE_NAME = "durable-queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 创建连接
        ConnectionFactory factory = new ConnectionFactory();
        // 设置 RabbitMQ 的主机名
        factory.setHost("localhost");
        // 创建一个连接
        Connection connection = factory.newConnection();
        // 创建一个通道
        Channel channel = connection.createChannel();
        // 创建一个Exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "direct", true);
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

        // 发送消息
        String message = "durable exchange test";
        AMQP.BasicProperties props = new AMQP.BasicProperties().builder().deliveryMode(2).build();
        channel.basicPublish(EXCHANGE_NAME, "", props, message.getBytes());

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

6. Precautions

1) can theoretically all the messages are set to persist, but it will seriously affect the performance of RabbitMQ. Because of the slow speed of writing to disk more than a little more than write speed memory. For reliability is not so high persistent message may not be employed to improve overall process throughput. In the choice of whether you want the message persistence, you need to make a trade-off between reliability and throughput.

2)将交换器、队列、消息都设置了持久化之后仍然不能百分之百保证数据不丢失,因为当持久化的消息正确存入RabbitMQ之后,还需要一段时间(虽然很短,但是不可忽视)才能存入磁盘之中。如果在这段时间内RabbitMQ服务节点发生了宕机、重启等异常情况,消息还没来得及落盘,那么这些消息将会丢失。

3)单单只设置队列持久化,重启之后消息会丢失;单单只设置消息的持久化,重启之后队列消失,继而消息也丢失。单单设置消息持久化而不设置队列的持久化显得毫无意义。

7. 源码

源码地址:https://github.com/zwwhnly/springboot-action.git,欢迎下载。

8. 参考

《RabbitMQ实战指南》

Guess you like

Origin www.cnblogs.com/zwwhnly/p/10948024.html