在应用开发中,持久化也是经常被提起的,持久化就是存在在内存当中的数据,要写入到磁盘中,这样在内存中的数据由于各种原因丢失掉后,可以通过磁盘重新加载到内存中来,对于消息中间件,持久化也至关重要。在生产者发布消息到broker后,消费者消费消息之前,消息是存在于内存当中,倘若消息丢失,那么就会造成消息的不一致性,这时就要对生产之后消费之前的消息做一下持久化操作。
RabbitMQ的持久化分为三个部分:交换器的持久化、队列的持久化和消息的持久化,而我们要关注的就是消息的持久化。但是消息是依附于队列而存在的,所以也要关注队列的持久化,倘若队列没有做持久化而丢失了,那么消息还能依附到哪里呢?但是队列的持久化能保证其本身的元数据不会因异常情况而丢失,但是并不能保证内部所存储的 消息不会丢失。要确保消息不会丢失,需要将其设置为持久化。通过将消息的投递模式 (BasicProperties中的deliveryMode属性)设置为2,即可实现消息的持久化。代码中消息投递时设置的MessageProperties.PERSISTENT_TEXT_PLAIN实际上是封装了这个属性:
设置了队列和消息的持久化,当RabbitMQ服务重启之后,消息依旧存在。单单只设置队 列持久化,重启之后消息会丢失;单单只设置消息的持久化,重启之后队列消失,继而消息也丢失。单单设置消息持久化而不设置队列的持久化显得毫无意义。这时要注意一个问题,如果将所有的消息都设为持久化,会严重影响RabbitMQ的性能。写入 磁盘的速度比写入内存的速度慢得不只一点点。所以对于可靠性不是那么高的消息可以不采用持久 化处理,以提高整体的吞吐量。在选择是否要将消息持久化时,需要在可靠性和吐吞量之间做一个权衡。
那么队列、消息都设置了持久化之后,就能百分百保证数据不丢失了吗?
在持久化的消息正确发送到RabbitMQ之后,还需要有一段时间(虽然很短〉才能存入磁盘之中。RabbitMQ并不会为每条消息都进行同步存盘的处理,可能仅仅保存到操作系统缓存之中而不是物理磁盘之中。如果在这段时间内RabbitMQ 服务节点发生了岩机、重启等异常情况,消息保存还没来得及落盘,那么这些消息将会丢失。所以针对上面那个问题,答案是否定的。
对于这种问题的出现,需要保证RabbitMQ的高可用,来增加RabbitMQ的可靠性,也就是要引入RabbitMQ的镜像队列机制,进行主从配置,如果主节点master 在此特殊时间内挂掉,可以自动切换到从节点slave, 这样有效地保证了高可用性,除非整个集群都挂掉。虽然这样也不能完全保证RabbitMQ消息 不丢失,但是配置了镜像队列要比没有配置镜像队列的可靠性要高很多,在实际生产环境中的关键业务队列一般都会设置镜像队列。
还有一点就是消费者消费消息,如果在订阅消费队列时将autoAck参数设置为true,那么当消费者接 收到相关消息之后,还没来得及处理就看机了,这样也算数据丢失。这种情况很好解决,将autoAck参数设置为false,并进行手动确认,这点和生产者投递消息保证一致性的处理机制,有着异曲同工之妙,就不再赘述了!
这样的保证消息一致性的解决方案,完全是靠着RabbitMQ的自身机制来解决的,然而这样的机制并不能保证消息百分之一百的不丢失,只是对于RabbitMQ来说已经尽力了,因为此时这样的消息系统,已经接近保证消息的一致性了,要想再更一步的百分百的保证,还要借助于外力来解决,具体要借助于怎样的外力,那么就要靠大家集思广益了!原谅我的能力不足!