前面我们讲了消费端处理消息时,消息如何不丢失,但怎么保证当RabbitMQ服务停掉之后,生产者发送的消息不丢失呢?
交换机持久化
交换机持久化是通过在声明交换机时将durable参数置为true实现的,相当于将交换机的属性在服务器内部保存,当MQ的服务器发生意外挂到之后,重启RabbitMQ时不需要重新去建立交换机,交换机会自动建立,相当于一直存在。
如果不设置持久化,那么服务重启之后,相关交换机元数据会丢失
ExchangeBuilder. topicExchange (Constant. ACK_EXCHANGE_NAME ).durable( true ).build()
队列持久化
队列持久化是通过在声明队列时将durable参数置为true实现的.
如果队列不设置持久化,那么在RabbitMQ服务重启之后,该队列就会被删掉,此时数据也会丢失.
队列的持久化能保证该队列本身的元数据不会因异常情况而丢失,但是并不能保证内部所有存储消息不会丢失,要确保消息不会丢失,需要将消息设置为持久化.
QueueBuilder. durable (Constant. ACK_QUEUE ).build()(持久化队列创建)
QueueBuilder.nonDurable(Constant.ACK_QUEUE).build()(非持久化队列创建)
消息持久化
消息实现持久化,需要把消息的投递模式(MessageProperties中的deliverMode)设置为2
设置了队列和消息的持久化, 当
RabbitMQ
服务重启之后, 消息依旧存在. 如果只设置队列持久化, 重启之后消息会丢失. 如果只设置消息的持久化, 重启之后队列消失, 继⽽消息也丢失. 所以单单设置消息持久化⽽不设置队列的持久化显得毫⽆意义.
将交换器、队列、消息都设置了持久化之后就能百分之百保证数据不丢失了吗? 答案是否定的.
1.
从消费者来说, 如果在订阅消费队列时将autoAck参数设置为true, 那么当消费者接收到相关消息之后, 还没来得及处理就宕机了, 这样也算数据居丢失. 这种情况很好解决, 将autoAck参数设置为
false, 并进⾏⼿动确认,详细可以参考[消息确认]章节.
2.
在持久化的消息正确存⼊RabbitMQ之后,还需要有⼀段时间(虽然很短,但是不可忽视)才能存⼊磁盘中.RabbitMQ并不会为每条消息都进⾏同步存盘(调⽤内核的fsync⽅法)的处理, 可能仅仅保存到操作系统缓存之中⽽不是物理磁盘之中. 如果在这段时间内RabbitMQ服务节点发⽣了宕机、重启等异常情况, 消息保存还没来得及落盘, 那么这些消息将会丢失.
那么这个问题怎么解决呢?
1.
引⼊RabbitMQ的仲裁队列(后⾯再讲), 如果主节点(master)在此特殊时间内挂掉, 可以⾃动切换到从节点(slave),这样有效地保证了⾼可⽤性, 除⾮整个集群都挂掉(此⽅法也不能保证100%可靠, 但是配置了仲裁队列要⽐没有配置仲裁队列的可靠性要⾼很多, 实际⽣产环境中的关键业务队列⼀般都会设置仲裁队列).
2.
还可以在发送端引⼊事务机制或者发送⽅确认机制来保证消息已经正确地发送并存储⾄RabbitMQ中.这就是我们下一篇博客要讲的发送方确认。