RabbitMQ 高级特性——持久化

打印 上一主题 下一主题

主题 925|帖子 925|积分 2775



  
前言

前面我们学习了 RabbitMQ 的高级特性——消息确认,消息确认可以保证消息传输过程的稳定性,但是在保证了消息传输过程的稳定性之后,还存在着其他的问题,我们都知道消息都存放在 RabbtiMQ Broker 中的队列中的,如果我们的 RabbitMQ Server 发生了重启或者宕机了,那么我们内存中的队列也就丢失了,相信大家也应该知道怎样办理这种问题,对了,那就是持久化。
持久化

RabbitMQ 的持久化分为三个部分:交换机的持久化、队列的持久化和消息的持久化。
交换机持久化

交换机的持久化是我们在声明交换机的时间,将 durable 的参数设置为 true 实现的。也就是将交换机的内部属性在服务器的内部保存,当 MQ 服务器发生重启之后,不必要去重新创建交换机,交换机会根据服务器中保存的交换机的属性来自动创建。
如果交换机不设置持久化,那么当 RabbitMQ 服务重启之后,交换机的元数据就会丢失,那么要想再使用这个交换机就只能重新创建这个交换机。

对于我们之前设置的持久化的交换机,如果我们重启 RabbitMQ Server,看一下这些交换机是否还会存在:
重启 RabbitMQ Server:systemctl restart rabbitmq-server.service

可以发现这些设置了持久化的交换机在 RabbitMQ Server 重启之后照旧存在的,也不能说是存在,只是重启的时间自动创建了,那么我们再来看看未被设置为持久化的队列在 RabbitMQ Server 重启之后是否会自动创建:
  1. @Bean("noPermanentExchange")
  2. public TopicExchange noPermanentExchange() {
  3.     return ExchangeBuilder.topicExchange(Constants.NO_PERMANENT_EXCHANGE).durable(false).build();
  4. }
复制代码
启动一下程序,创建这个交换机:

然后重启一下 RabbitMQ Server:

重启服务之后,我们创建的非持久化的交换机就不会自动创建了。
队列持久化

队列的持久化也是我们在声明队列的时间设置 durable 的参数来实现的。如果队列不设置持久化,那么当我们的 RabbitMQ Server 重启的时间,这些未设置持久化的队列就会丢失,那么队列中的消息也就会丢失(不管队列内里的消失是否设置了持久化)。
队列的持久化能保证队列的元数据不会因非常情况而丢失,但是并不能保证内部所存储的消息不会丢失,要确保消息不会丢失,还必要设置消息为持久化。
QueueBuilder.durable(Constants.ACK_QUEUE).build(); 创建持久化队列;
QueueBuilder.nonDurable(Constants.ACK_QUEUE).build(); 创建非持久化队列,durable 参数默认是 true,也就是队列默认是持久化的队列。
消息持久化

实现消息持久化,必要把消息的投递模式(MessageProperties 中的 deliveryMode)设置为2,也就是 MessageDeliveryMode.PERSISTENT。
  1. public enum MessageDeliveryMode {
  2.         NON_PERSISTENT,//非持久化
  3.         PERSISTENT;//持久化
  4. }
复制代码
消息存储在队列中,既然存储在队列中,那么要想真正实现消息的持久化,就也必要保证队列的持久化。如果队列不持久化,消息持久化,那么当 RabbitMQ 服务重启的时间,队列就会消息,更不用说内里的消息了,当队列持久化,但是消息不持久化的话,那么服务重启,队列存在但是队列中的消息不存在,也就是说只有队列和消息都设置为持久化才气真正实现消息的持久化。
  1. public static final String PERMANENT_EXCHANGE = "permanent.exchange";
  2. public static final String PERMANENT_QUEUE = "permanent.queue";
复制代码
声明队列、交换机以及队列和交换机的绑定关系:
  1. @Bean("permanentQueue")
  2. public Queue permanentQueue() {
  3.     return QueueBuilder.durable(Constants.PERMANENT_QUEUE).build();
  4. }
  5. @Bean("permanentExchange")
  6. public TopicExchange permanentExchange() {
  7.     return ExchangeBuilder.topicExchange(Constants.PERMANENT_EXCHANGE).build();
  8. }
  9. @Bean("permanentBinding")
  10. public Binding permanentBinding(@Qualifier("permanentExchange") Exchange exchange, @Qualifier("permanentQueue") Queue queue) {
  11.     return BindingBuilder.bind(queue).to(exchange).with("permanent").noargs();
  12. }
复制代码
注意:当进行队列和交换机的绑定的时间,如果参数中交换机的类型是 Exchange 的话,创建的 Binding 实例中就还必要加上 noargs 方法。
生产者代码:
  1. @RequestMapping("/permanent")
  2. public String permanent() {
  3.     String msg = "rabbitmq permanent";
  4.     Message message = new Message(msg.getBytes(),new MessageProperties());
  5.     message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
  6.     rabbitTemplate.convertAndSend(Constants.PERMANENT_EXCHANGE,"permanent",message);
  7.     return "消息发送成功";
  8. }
复制代码
我们先不实现消费者,这里只是为了看队列中的消息是否实现了持久化。
我们先向队列中生产几条消息,然后再重启服务,看当队列和消息都设置为持久化的时间是否能真正实现消息的持久化:

然后重启 RabbitMQ 服务:

重启之后发现,队列中的消息照旧存在的,那么如果我们将队列设置为非持久化,消息设置为持久化呢?
由于 RabibtMQ 交换机和队列只要创建了那么就不能修改它的属性了,所以我们这里先将之前创建的队列删除重新创建:
  1. @Bean("permanentQueue")
  2. public Queue permanentQueue() {
  3.     return QueueBuilder.nonDurable(Constants.PERMANENT_QUEUE).build();
  4. }
复制代码

重启服务:
注意当我们重启 RabbitMQ 的时间,必要将我们的程序也关闭掉,不然当我们重启 RabbitMQ 服务的时间,程序就会自动毗连上 RabbitMQ 然后自动创建交换机和队列:
我们的程序起首会报错:

然后服务重启乐成之后,就会自动毗连 RabbitMQ 并且创建交换机和队列:


所以我们生产完成几条消息之后,关闭程序然后重启服务:

重启服务之后:

可以看到我们的队列没有实现持久化,整个队列都没了,更别说队列内里的消息了,所以只有队列和消息都实现持久化的时间才气真正实现消息的持久化。
实现消息的持久化的时间,不能将全部的消息都持久化,由于写硬盘的速度是非常慢的,我们应该按需按情况实现部分消息的持久化。
将交换机、队列和消息实现持久化就能百分百保证数据不丢失了吗?答案是否定的。

  • 从消费者来说,如果在订阅消费队列时将 autoAck 设置为 true,那么当消费者接收到相关的消息之后,还没来得及处理就宕机了,这样也算数据丢失,这种情况很好办理,就是将 autoAck 设置为 false,手动确认就好了
  • 在持久化的消息精确存入 RabbitMQ 之后,还必要一段时间(虽然很短,但是也不能忽视)才气存入磁盘中,RabbitMQ 并不会为每条消息都进行同步存盘(调用内核的 fsync 方法)的处理,可能仅仅保存到操作体系的缓存之中,而不是物理磁盘上,如果在这个时间段内 RabbitMQ 发生了宕机、重启等非常,那么消息还没来得及落盘,那么这些消息就会丢失
那么第二个问题怎样办理呢?

  • 引入RabbitMQ的仲裁队列(后面再讲),如果主节点(master)在此特别时间内挂掉,可以自动切换到从节点(slave),这样有用地保证了高可用性。除非整个集群都挂掉(此方法也不能保证100%可靠,但是设置了仲裁队列要比没有设置仲裁队列的可靠性要高很多。实际生产情况中的关键业务队列一样寻常都会设置仲裁队列)。
  • 还可以在发送端引入事务机制或者发送方确认机制来确保消息已经精确地发送并存储至RabbitMQ中。详细参考下一个章节内容先容——“发送方确认”。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

数据人与超自然意识

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表