1 队列持久化
RabbitMQ的队列分为两种,持久化(durable)和瞬时(transient)队列。一个节点重启后,会重新声明持久化队列。但持久化队列内的消息是否可以被恢复,取决于消息本身的持久性
队列的持久化在需要客户端声明队列时配置
现在实战一下
1.1 客户端声明队列为持久化
java的RabbitMQ客户端,channel.queueDeclare()第二个参数表示是否持久化,运行下列代码,可以看到web页面上多了一个名为durable_test的持久化队列
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class CreateDurableQueue {
private final static String QUEUE_NAME = "durable_test";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("服务器IP"); // 设置服务器
factory.setUsername("admin"); // 账号
factory.setPassword("password"); // 密码
try (Connection connection = factory.newConnection(); // 创建连接和通道
Channel channel = connection.createChannel()) {
channel.queueDeclare(QUEUE_NAME, true, false, false, null); // 声明持久化队列
}
}
}
1.2 测试队列持久化
现在往队列durable_test中发送一条消息后重启服务器
重启前,队列中有一条未消费消息
关机后,这个队列呈现down的状态
重启后,队列中消息不存在
1.3 非持久化队列在服务器重启后
一个非持久化队列在节点重启后直接从队列列表中消失
2 消息持久化
持久化消息必须处在一个持久化的队列中才是有意义的。配置持久化消息的唯一方法是客户端在发送消息时配置delivery_mode为2
消息持久化并不是可靠的,因为可能在消息还没有完成持久化前服务器因为故障停机,重启节点后消息还是会丢失。最安全的方法是confirm机制,可以参见我的另一篇博文Centos6下RabbitMQ学习(一)——Java客户端收发消息、可靠性、确认
之前已经测试过,非持久化的消息在服务器重启之后不能恢复,现在来看看持久化的消息
2.1 向非持久化队列发送持久化消息
声明一个非持久化队列,发送一个持久化消息,再把服务重启
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
public class SendPersistent {
private final static String QUEUE_NAME = "hello";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("服务器IP"); // 设置服务器
factory.setUsername("admin"); // 账号
factory.setPassword("password"); // 密码
try (Connection connection = factory.newConnection(); // 创建连接和通道
Channel channel = connection.createChannel()) {
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 声明非持久化队列
String message = "Hello World";
channel.basicPublish("", QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes()); // 创建新生产者,发送持久化文本消息
System.out.println(" [x] Sent '" + message + "'");
}
}
}
重启之后,非持久化队列都木有了,何况是消息呢,皮之不存毛将焉附
2.2 向持久化队列发送持久化消息
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
public class SendPersistent {
private final static String QUEUE_NAME = "hello";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("服务器IP"); // 设置服务器
factory.setUsername("admin"); // 账号
factory.setPassword("password"); // 密码
try (Connection connection = factory.newConnection(); // 创建连接和通道
Channel channel = connection.createChannel()) {
channel.queueDeclare(QUEUE_NAME, true, false, false, null); // 声明非持久化队列
String message = "Hello World";
channel.basicPublish("", QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes()); // 创建新生产者,发送持久化文本消息
System.out.println(" [x] Sent '" + message + "'");
}
}
}
妥妥的
2.3 镜像队列持久化消息
镜像队列的情况呢,官网文档我还没有找到相关的说明,但是我做了一个测试
在exactly镜像队列模式下,集群中有节点1、2、3,原来的master队列在2上,镜像队列在节点3,向队列发送两条持久化消息
然后首先挂掉节点2,再挂掉节点1,最后挂掉节点3
按节点2、1、3的顺序重启节点,出现的情况是,节点3成为master,节点2成为mirror,且节点2未同步,也就是说节点2的镜像队列是空的
我们可以得出一个结论,即只有master队列重启后,能恢复持久化的消息