RabbitMQ的几个常见问题

1. 如何保证消息尽量发送成功?

问题描述: 如果没有启动消费者,重启了RabbitMQ服务,队列和消息都会丢失。

解决方案: 针对这个问题,有以下几个机制可以解决:

  1. 生产者确认;
  2. 持久化;
  3. 手动ACK。

生产者确认

首先,我们要确保生产者能成功地将消息发送到RabbitMQ服务器。
默认情况下生产者发送消息并不会返回任何状态信息,也就是它并不知道消息有没有正确地到达服务器。

针对这个问题,RabbitMQ提供了两种解决方案:

  1. 事务机制;
  2. 通过发送方确认机制(publisher confirm);

事务机制相关的方法主要有三个:

  1. channel.txSelect:用于将当前的channel设置成事务模式;
  2. channel.txCommit:用于提交事务;
  3. channel.txRollback:用于回滚事务.

用过数据库的人对事务一词肯定不陌生,在RabbitMQ中也是类似的,只有消息被RabbitMQ服务端成功接收,事务才能提交成功,否则就会触发异常,可以对异常进行捕获处理。
事务机制是阻塞形式的,一条消息发送之后会使消息端阻塞,以等待RabbitMQ的回应,才能发送下一个消息。
使用事务机制会影响RabbitMQ的性能,因此还是推荐使用发送方确认机制。

发送方确认机制是指生产者将channel设置成confirm模式,所有在该信道上发布的消息都会被指派一个唯一ID(从1开始),一旦消息被投递到RabbitMQ服务器之后,RabbitMQ就会发送一个包含了消息唯一ID的确认(Basic.Ack)给生产者,使生产者知道消息已经正确到达了目的地。
如果RabbitMQ因为内部错误导致消息丢失,就会发送一条nack(Basic.Nack)命令,生产者可以在回调方法中处理该nack命令。
发送方确认机制是异步的,一旦发布一条消息,生产者可以在等待信道返回的同时发送下一条消息,当消息确认时,可以在回调方法中处理该确认消息。因此,总体效率会更好。
相关的方法:

  1. channel.confirmSelect();
  2. channel.waitForConfirms;
  3. channel.addConfirmListener;

当然confirm机制也可以分为:普通confirm,批量confirm,异步confirm。
普通confirm可事务机制差不多,就是发送一条,就调用waitForConfirms确认一次;批量confirm就是发送多条后,再调用waitForConfirms确认一次;异步confirm就是注册两个回调分别处理Ack和NAck确认。

事务机制和生产者确认机制是互斥的,不能共存!

发送成功的含义是消息能到达RabbitMQ交换机,并且能有匹配的队列接收。

总体效率上,异步confirm会更好。

2. 如何进行消息持久化?

所谓持久化,就是RabbitMQ将内存中的数据(比如交换机、队列、消息等)固化到磁盘,以防止异常情况的发生时造成数据丢失。

RabbitMQ持久化分为:

  1. 交换机持久化;
  2. 队列持久化;
  3. 消息持久化

交换机的持久化

在创建Exchange时设置durable参数参数。

channel.exchangeDeclare(EXCHANGE_NAME, "direct", true);

队列的持久化

同样也是设置设置durable参数。
持久化的队列会存盘,在服务器重启的时候可以保证不丢失相关信息。

channel.queueDeclare(QUEUE_NAME, true, false, false, null);

消息的持久化

即使交换机、队列持久化不会因为重启丢失,但是存储在队列中的消息仍然会丢失。
解决的办法就是设置消息的投递模式为2,即代表持久化(JAVA)。

理论上,可以将所有的消息都设置为持久化,但是这会严重影响RabbitMQ性能,因为写入到磁盘的速度可比写入到内存的速度慢非常多。因此,在选择是否要持久化消息时,需要在可靠性和吞吐量之间做一个权衡。

然而,将交换机、队列、消息都设置持久化之后仍然不能够保证百分百不会丢失数据。因为有些消息可能还没来得及落盘,就发生了宕机、重启等异常情况。

如何保证消息被正确消费

这部分要处理的场景是: 当消费者接收到消息后,还没处理完业务逻辑,消费者挂掉了,此时消息等同于丢失了。

为了确保消息被消费者成功消费,RabbitMQ提供了消息确认机制,主要通过显示Ack模式来实现。

默认情况下,RabbitMQ会自动把发送出去的消息置为确认,然后从内存(或磁盘)删除,但是我们在使用时可以手动设置autoAck为False的,当然具体做法各个语言都不一样。

需要注意的时,如果设置autoAck为false,也就意味者每条消息需要我们自己发送ack确认,RabbitMQ才能正确标识消息的状态。

消息队列的优缺点

优点:

  1. 解耦和复用,易扩展;
  2. 异步;
  3. 削峰;

缺点:

  1. 系统可用性降低,复杂度提高,mq要是挂了,影响太大;
  2. 存在数据一致性问题

选用RabbitMQ的理由

目前还是有很多种消息队列的,各有特点。
对于选用RabbitMQ的理由,大概因为:延时低是它最大的特点,同时单机吞吐量也很不错;也能进行分布式集群扩展;社区非常活跃。

posted @ 2019-06-03 20:55  星星,风,阳光  阅读(3963)  评论(0编辑  收藏  举报