RabbitMQ 教程

持久化(Persistence)

持久化(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。

持久化是将程序数据在持久状态和瞬时状态间转换的机制。

持久化可以提高 RabbitMQ 的可靠性,以防止在异常情况(重启、关闭、宿机等)下的数据丢失。RabbitMQ 的持久化分为三个部分:交换器持久化、队列持久化和消息持久化

交换器(Exchange)持久化

交换器的持久化是通过将声明交换器方法 exchangeDeclare() 中的 durable 参数置为 true 实现的,方法定义如下:

Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable, boolean autoDelete, Map<String, Object> arguments) throws IOException;

上面方法中的 durable 参数就是用来设置交换器是否持久化。

如果设置为 true,则开启持久化功能,持久化可以将交换器信息存储到磁盘,重启 RabbitMQ 服务后交换器信息不会丢失。

如果设置为 false,则不开启持久化功能,那么在 RabbitMQ 服务重启之后,相关的交换器元数据会丢失,不过消息不会丢失。只是不能将新的消息发送到该交换器中了。如果需要长期使用的交换器,建议将其设置为持久化。

队列(Queue)持久化

队列的持久化是通过将声明队列方法 queueDeclare() 中的 durable 参数置为 true 实现的,方法定义如下:

Queue.DeclareOk queueDeclare(String queue, boolean durable , boolean exclusive,  boolean autoDelete , Map<String, Object> arguments) throws IOException;

上面方法中的 durable 参数就是用来设置队列是否持久化。

如果设置为 true,则队列开启持久化功能。此时,队列的持久化能保证其本身的元数据不会因异常情况而丢失,但是并不能保证内部所存储的消息不会丢失。

如果设置为 false,则队列不开启持久化功能。那么在 RabbitMQ 服务重启之后,队列相关的元数据会丢失,存储在队列中的消息也会丢失。

消息(Message)持久化

消息持久化可以通过将消息的投递模式 (BasicProperties 中的 deliveryMode 属性)设置为 2 即可实现消息的持久化。前面章节使用的 MessageProperties.PERSISTENT_TEXT_PLAIN 实际上就封装了这个属性,如下:

/** Content-type "text/plain", deliveryMode 2 (persistent), priority zero */
public static final BasicProperties PERSISTENT_TEXT_PLAIN =
    new BasicProperties("text/plain",
                        null,
                        null,
                        2,
                        0, null, null, null,
                        null, null, null, null,
                        null, null);

例如:

// 发送消息
System.out.println("[Send] Sending Message...");
boolean mandatory = true;
byte[] msg = "hello wrold".getBytes();
channel.basicPublish(EXCHANGE_NAME, "www.hxstrive.com",
        mandatory, MessageProperties.PERSISTENT_TEXT_PLAIN, msg);

同时将队列和消息设置为持久化,当重启 RabbitMQ 服务后,消息依旧存在。如果仅仅将队列设置为持久化,重启之后消息会丢失;如果仅仅将消息设置为持久化,重启之后队列消失,而且消息也丢失;

注意:可以将所有的消息都设直为持久化,但是这样会严重影响 RabbitMQ 的性能(随机)。因为写入磁盘的速度比写入内存的速度慢得不只一点点。对于可靠性不是那么高的消息可以不采用持久化处理以提高整体的吞吐量。在选择是否要将消息持久化时,需要在可靠性和吐吞量之间做一个权衡。

如果真的将交换器、队列、消息都设置了持久化之后就能百分之百保证数据不丢失了吗?答案是否定的。有如下情况可能会导致数据丢失:

  • 如果在订阅消费队列时将 autoAck(自动队列)参数设置为 true ,那么当消费者接收到相关消息之后,还没来得及处理就宕机了,那么这条消息就丢失了。这种情况很好解决,将 autoAck 参数设置为 false ,并进行手动确认即可。

  • 将持久化消息发送到 RabbitMQ 服务之后,还需要有一段时间(虽然很短,但是不可忽视〉才能存入磁盘之中。RabbitMQ 并不会为每条消息都进行同步存盘的处理,可能仅仅保存到操作系统缓存之中而不是物理磁盘之中。如果在这段时间内 RabbitMQ 服务节点发生了岩机、重启等异常情况,消息保存还没来得及落盘,那么这些消息将会丢失。

    那么,上面的问题怎么解决呢?这里可以引入 RabbitMQ 镜像队列机制,相当于配置了副本,如果主节点(master)在此特殊时间内挂掉,可以自动切换到从节点(slave),这样有效地保证了高可用性,除非整个集群都挂掉。虽然这样也不能完全保证 RabbitMQ 消息不丢失,但是配置了镜像队列要比没有配置镜像队列的可靠性要高很多,在实际生产环境中的关键业务队列一般都会设置镜像队列。

  • 最后,还可以在发送端引入事务机制或者发送方确认机制来保证消息己经正确地发送并存储至 RabbitMQ 中,前提还要保证在调用 channel.basicPublish() 方法的时候交换器能够将消息正确路由到相应的队列之中。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号