一、队列持久化的概念
队列的声明默认是存放到内存中的,如果rabbitmq重启会丢失,如果想重启之后还存在就要使队列持久化,保存到Erlang自带的Mnesia数据库中,当rabbitmq重启之后会读取该数据库。
二、排他
简单理解就是在连接关闭时是否会删除队列(无论队列中有没有消息)
三、自动删除
当队列中有消息时,无论是否排他,关闭连接都不会删除队列,此时消费者消费完消息后再断开消费者,队列会被自动删除。(这里如果有多个消费者消费同一个队列,则需要所有消费者都断开后才能自动删除)
四、消息确认机制 Message acknowledgment
在实际应用中,可能会发生消费者收到Queue中的消息,但没有处理完成就宕机(或出现其他意外)的情况,这种情况下就可能会导致消息丢失。为了避免这种情况发生,我们可以要求消费者在消费完消息后发送一个回执给RabbitMQ,RabbitMQ收到消息回执(Message acknowledgment)后才将该消息从Queue中移除;如果RabbitMQ没有收到回执并检测到消费者的RabbitMQ连接断开,则RabbitMQ会将该消息发送给其他消费者(如果存在多个消费者)进行处理。这里不存在timeout概念,一个消费者处理消息时间再长也不会导致该消息被发送给其他消费者,除非它的RabbitMQ连接断开。 这里会产生另外一个问题,如果我们的开发人员在处理完业务逻辑后,忘记发送回执给RabbitMQ,这将会导致严重的bug——Queue中堆积的消息会越来越多;消费者重启后会重复消费这些消息并重复执行业务逻辑…。pub message是没有ack的。
消息一旦被消费者接收,队列中的消息就会被删除。RabbitMQ怎么知道消息被接收了呢?
如果消费者领取消息后,还没执行操作就挂掉了呢?或者抛出了异常?消息消费失败,但是RabbitMQ无从得知,这样消息就丢失了!
因此,RabbitMQ有一个ACK机制。当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。
五、消息ACK的两种情况
- 自动ACK
- 消息一旦被接收,消费者自动发送ACK 如果消息不太重要,丢失也没有影响,那么自动ACK会比较方便
- 手动ACK
- 消息接收后,不会发送ACK,需要手动调用 如果消息非常重要,不容丢失。那么最好在消费完成后手动ACK,否则接收消息后就自动ACK,RabbitMQ就会把消息从队列中删除。如果此时消费者宕机,那么消息就丢失了。
六、Rabbitmq消费消息的模式
rabbitmq的消费模式分为两种: 推(Push)模式和拉(Pull)模式。推模式采用Basic.Consume进行消费,而拉模式则是调用Basic.Get模式。
Push模式
mq主动将消息推送给消费者(消费者需提供一个消费接口)
mq属于主动方,消费者属于一种被动消费,一旦有消息到达mq,会触发mq推送机制,将消息推送给消费者,不管消费者处于何种状态。
优点:
- 消费者代码较少:对于消费者来说,只需提供一个消费接口给mq即可;mq将接收到的消息,随即推送到指定的消费接口
- 消息实时性比较高:对于消费者来说,消息一旦到达mq,mq会立即推送给消费者
缺点:
- 消费者属于被动方,消息量比较大时,对消费者性能要求比较高;若消费者机器资源有限,可能会导致压力过载,引发宕机的情况。
- 对消费者可用性要求比较高:当消费者不可用时,会导致很push失败,在mq方需要考虑至少推送成功一次。
Pull模式
消息消费的过程:
- 消费端采用轮询的方式,从mq服务中拉取消息进行消费
- 消费完成通知mq删除已消费成功的消息
- 继续拉取消息消费
对于消费者来说,是主动方,可以采用线程池的方式,根据机器的性能来增加或缩小线程池的大小,控制拉取消息的速度,可以很好的控制自身的压力。
优点:
- 消费者可以根据自己的性能主动控制消息拉去的速度,控制自己的压力,不至于把自己弄跨
- 实时性相对于push方式会低一些
- 消费者属于主动方,控制权更大一些
缺点:
- 消费方需要实现消息拉取的代码
- 消费速度较慢时,可能导致mq中消息积压,消息消费延迟等。
七、消费模式的最佳实践
- 消费者性能较好,对实时性要求比较高的,可以采用push的方式
- 消费者性能有限,建议采用pull的方式
- 整体上来说,主要在于消费者的性能,机器的性能如果没有问题,push和pull都是可以的