RabbitMq的持久化操作包含有交换机持久化、队列持久化和消息持久化三类。关于本文的测试大家可以参考我前两篇文章:Spring Boot整合RabbitMQ实现分布式服务的模式。
链接: RabbitMQ笔记(一)SpringBoot整合RabbitMQ之simple容器(消费者).
链接: RabbitMQ笔记(二)SpringBoot整合RabbitMQ之simple容器(生产者).
在使用RabbitMQ时,如果使用到发布订阅模式、路由模式(direct)或者主题模式(top)时,如果交换器不设置持久化,如果在特殊情况下(网络、磁盘、内存或者迁移等),需要重启RabbitMQ服务,相关的交换机元数据会丢失,不过消息不会丢失,只是不能将消息发送到该 Exchange 。所以如果RabbitMQ如果是你服务里一个重要的中间件,那么持久化还是有其必要性的。如何持久化呢?先看下源码,我这里以路由模式(direct)为例(我这里的整合的版本是 2.5.2,具体的可以考察上面提到的两篇文章):
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>2.5.2version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
<version>2.5.2version>
dependency>
DirectExchange.java
package org.springframework.amqp.core;
import java.util.Map;
public class DirectExchange extends AbstractExchange {
public static final DirectExchange DEFAULT = new DirectExchange("");
public DirectExchange(String name) {
super(name);
}
public DirectExchange(String name, boolean durable, boolean autoDelete) {
super(name, durable, autoDelete);
}
public DirectExchange(String name, boolean durable, boolean autoDelete, Map<String, Object> arguments) {
super(name, durable, autoDelete, arguments);
}
public final String getType() {
return "direct";
}
}
这里我们关注的是第二个方法public DirectExchange(String name, boolean durable, boolean autoDelete),交换机的持久:只需要在 DirectExchange 的构造函数把参数durable设置为true。
/**
* 定义交换机(持久化)
*
* name:交换机的名称
* durable:设置是否持久化。持久化可以将交换机存盘,在RabbitMQ服务重启的时候不会丢失相关信息
* autoDelete:在所在消费者都解除订阅的情况下自动删除
*/
@Bean
public DirectExchange defaultExchange() {
return new DirectExchange(MQConstants.ALIAN_EXCHANGE_NAME, true, false);
}
和交换机持久化一样,队列持久化只需要在 Queue 的构造函数 public Queue(String name, boolean durable) 把 durable 参数置为 true 就可实现。如果队列不设置持久化( (durable 默认为 false),那么在RabbitMQ 服务重启之后,相关队列的元数据会丢失,此时数据也会丢失。如果是很重要的信息就会出现生产事故了,所以队列的持久化也是很有必要的。
/**
* 定义一个队列(持久化)
*
* name:队列的名称
* durable:设置是否持久化。持久化的队列会存盘,在RabbitMQ服务重启的时候可以保证不丢失相关信息
*
* @return
*/
@Bean
public Queue aLianQueue() {
return new Queue(MQConstants.ALIAN_QUEUE_NAME, true);
}
对于路由模式设置了Exchange 和 Queue 持久化以后,当 RabbitMQ 服务重启之后,交换机和队列依然存在,但消息已经消失,可见仅仅设置交换机和队列的持久化而不设置消息持久化就没有什么意义,所以我们通常列队持久化会与消息持久化一起使用。那么如果实现消息持久化呢?我们只需要发送消息的时候将 deliveryMode设置为2,也就是设置为MessageDeliveryMode.PERSISTENT,其中(NON_PERSISTENT=1,PERSISTENT=2)如下例:
package com.alian.publish.service;
import com.alian.common.constant.MQConstants;
import com.alian.common.dto.PayRecord;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.time.LocalDateTime;
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestPersistenceService {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void sendPPersistence() {
PayRecord payRecord = new PayRecord();
payRecord.setPayTranSeq("2021080314723658965");
payRecord.setPayAmount(5000);
payRecord.setPayType("01");
payRecord.setStatus("00");
payRecord.setPayTime(LocalDateTime.now());
payRecord.setPayNo("420125836485665587453489");
MessagePostProcessor processor = message -> {
//消息持久化设置(取值有两个 NON_PERSISTENT=1,PERSISTENT=2)
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return message;
};
//也可以自定义id,根据自己的业务需要,如:CorrelationData correlationData = new CorrelationData("2021080314723658965");
CorrelationData correlationData = new CorrelationData();
//发送消息
rabbitTemplate.convertAndSend(MQConstants.ALIAN_EXCHANGE_NAME, MQConstants.ALIAN_ROUTINGKEY_NAME, payRecord, processor, correlationData);
}
}
实际上呢,细心的小伙伴肯定会发现,2.0版本以后通过Spring Boot整合的RabbitMQ,不管是消息模板rabbitTemplate还是发送的消息对象Message最终都是用到了MessageProperties,而MessageProperties默认就是支持持久化的了 ,只不过大家自己设置觉得安心而已。
public class MessageProperties implements Serializable {
//省略部分参数
private MessageDeliveryMode deliveryMode;
private Integer priority;
public MessageProperties() {
this.deliveryMode = DEFAULT_DELIVERY_MODE;
this.priority = DEFAULT_PRIORITY;
}
//省略部分方法
static {
DEFAULT_DELIVERY_MODE = MessageDeliveryMode.PERSISTENT;
DEFAULT_PRIORITY = 0;
}
}
如果将所有的消息都设置为持久化,可能就会严重影响RabbitMQ的性能,如果消息的必要性和可靠性不是那么高的情况下,可以不使用持久化,用以提高服务的吞吐量。所以消息是否需要持久化时,要综合业务必要性,服务性能伸缩性做一个权衡。
以上就是RabbitMQ相关的持久化,实际上呢,持久化并不是那么的复杂,甚至说是比较简单了,只是缺少了一个整体的归纳而已。RabbitMQ持久化大部分情况下是必要的,但是也要结合业务和系统的性能综合考虑。