优先级队列
优先级队列 , 被消费的特权。
顾名思义,具有高优先级的队列具有高的优先权,优先级高的消息具备优先可以通过设置队列的 x -max-priority 参数来实现
默认最低为 0 ,最高为队列设置的最大优先级。优先 级高的消息可以被优先消费,这个也是有前提的 : 如果在消费者的消费速度大于生产者的速度
且 Broker 中没有消息堆积的情况下 , 对发送的消息设置优先级也就没有什么实际意义。因为生产者刚发送完一条消息就被消费者消费了,那么就相当于 Broker 中至多只有一条消息,对于单 条消息来说优先级是没有什么意义的。
可以将priority值设置为1到10,即您设置的最大优先级值。您可以将最大优先级值设置为255,但RabbitMQ建议最大值为10
注意: 您不能将队列重新声明为优先级队列。您必须删除旧队列并将新队列声明为优先级队列。
持久化
持久化可以提高 RabbitMQ 的可靠性,以防在异常情况(重启、关闭、右机等)下的数据丢失
RabbitMQ的持久化分为三个部分:
交换器的持久化、队列的持久化和消息的持久化 。
交换器的持久化是通过在声明队列是将 durable 参数置为 true 实现的
如果交换器不设置持久化,那么在 RabbitMQ 服务重启之后,相关的交换器元数据会丢失,不过消息不会丢失,只是不能将消息发送到这个交换器中了。对一个长期使用的交换器来说, 建议将其置为持久化的。
队列的持久化是通过在声明队列时将 durable 参数置为 true 实现的
如果队列不设置持久化,那么在 RabbitMQ 服务重启之后,相关队列的元数据会丢失,此时数据也会丢失。
队列的持久化能保证其本身的元数据不会因异常情况而丢失,但是并不能保证内部所存储的消息不会丢失。要确保消息不会丢失 , 需要将其设置为持久化。
设置了队列和消息的持久化,当 RabbitMQ 服务重启之后,消息依旧存在。单单只设置队 列持久化,重启之后消息会丢失;单单只设置消息的持久化,重启之后队列消失,继而消息也 丢失。单单设置消息持久化而不设置队列的持久化显得毫无意义。
注意要点:
可以将所有的消息都设直为持久化,但是这样会严重影响 RabbitMQ 的性能(随机)。写入磁盘的速度比写入内存的速度慢得不只一点点。对于可靠性不是那么高的消息可以不采用持久化处理以提高整体的吞吐量。在选择是否要将消息持久化时,需要在可靠性和吐吞量之间做一个权衡。
将交换器、队列、消息都设置了持久化之后就能百分之百保证数据不丢失了吗?
- 首先从消费者来说,如果在订阅消费队列时将 autoAck 参数设置为 true ,那么 当消费者接收到相关消息之后,还没来得及处理就看机了,这样也算数据丢失。这种情况很好解决,将 autoAck 参数设置为 false ,并进行手动确认
- 其次,在持久化的消息正确存入 RabbitMQ 之后,还需要有一段时间(虽然很短,但是不可忽视〉才能存入磁盘之中。 RabbitMQ 并不会为每条消息都进行同步存盘(调用内核的 fsyncl方法)的处理,可能仅仅保存到操作系统缓存之中而不是物理磁盘之中。如果在这段时间内 RabbitMQ 服务节点发生了岩机、重启等异常情况,消息保存还没来得及落盘,那么这些消息将会丢失。
- 这个问题怎么解决呢?
这里可以引入 RabbitMQ 的镜像队列机制),相当 于配置了副本,如果主节点 C master ) 在此特殊时间内挂掉,可以自动切换到从节点 C slave ) , 这样有效地保证了高可用性,除非整个集群都挂掉。虽然这样也不能完全保证 RabbitMQ 消息 不丢失,但是配置了镜像队列要比没有配置镜像队列的可靠性要高很多,在实际生产环境中的 关键业务队列一般都会设置镜像队列。
生产者确认
如果不进行特殊配置,默认情况下发送消息的操作是不会返 回任何信息给生产者的,也就是默认情况下生产者是不知道消息有没有正确地到达服务器。
RabbitMQ 针对这个问题,提供了两种解决方式:
- 通过事务机制实现:
- 通过发送方确认 C publisher confirm ) 机制实现
事务机制
RabbitMQ 客户端中与事务机制相关的方法有 三 个: channel. txSelect 、 channel.txCommit 和 channel . txRollback.
- channel.txSelect 用于将当前的信道设置成事务模式.
- channel . txCommit 用于提交事务
- channel . txRollback 用于事务回滚。
在通过 channel.txSelect 方法开启事务之后,我们便可以发布消息给 RabbitMQ 了, 如果事务提交成功,则消息一定到达了RabbitMQ 中,如果在事务提交执行之前由于 RabbitMQ 异常崩溃或者其他原因抛出异常,这个时候我们便可以将其捕获,进而通过执行channel . txRollback 方法来实现事务回夜。注意这里的 RabbitMQ 中的事务机制与大多数数据库中的事务概念井不相同,需要注意区分。
java代码
事务确实能够解决消息发送方和RabbitMQ之间消息确认的问题,只有消息成功被RabbitMQ接收,事务才能提交成功,否则便可在捕获异常之后进行事务回滚 ,与此同时可以进 行消息重发
发送方确认机制
采用事务机制实现会严重降低 RabbitMQ 的消息吞吐量及MQ的性能, 所以RabbitMQ 提供了 一个改进方案 ,即发送方确认机制
生产者将信道设置成 confirm (确认)模式,一旦信道进入 confmn 模式,所有在该信道上 面发布的消息都会被指派一个唯一的 ID(从 l 开始),一旦消息被投递到所有匹配的队列之后, RabbitMQ 就会发送一个确认 (Basic.Ack) 给生产者(包含消息的唯一ID) ,这就使得生产 者知晓消息已经正确到达了目的地了
如果消息和队列是可持久化的,那么确认消息会在消息 写入磁盘之后发出。
生产者通过调用 channel . confirmSelect 方法(即 Confirm.Select 命令)将信道设置为 confrrm 模式,之后 RabbitMQ 会返回 Confirm . Select-Ok 命令表示同意生产者将当 前信道设置为 confirm 模式 。
消息顺序性
- 消息的顺序性是指消费者消费到的消息和发送者发布的消息的顺序是一致的。举个例子 ,不考虑消息重复的情况,如果生产者发布的消息分别为 msgl、 msg2、 msg3 ,那么消费者必然 也是按照 msgl 、 msg2 、 msg3 的顺序进行消费的。
- 目前很多资料显示RabbitMQ 的消息能够保障顺序性,这是不正确的,或者说这个观点有很大的局限性。在不使用任何 RabbitMQ 的 高级特性,也没有消息丢失、网络故障之类异常的 情况发生,并且只有一个消费者的情况下,最好也只有一个生产者的情况下可 以保证消息的顺序性。如果有多个生产者同时发送消息,无法确定消息到达 Broker 的前后顺序,也就无法验证 消息的顺序性。
那么哪些情况下 RabbitMQ 的消息顺序性会被打破呢?下面介绍几种常见的情形。
- 如果生产者使用了事务机制,在发送消息之后遇到异常进行了事务回滚,那么需要重新补 偿发送这条消息,如果补偿发送是在另一个线程实现的 ,那么消息在生产者这个源头就出现了 错序。
- 如果启用 publisher confirrn 时,在发生超时、中断,又或者是收到 RabbitMQ 的 Basic.Nack 命令时,那么同样需要补偿发送,结果与事务机制一样会错序
- 如果生产者发送的消息设置了不同的超时时间,井且也设置了死信队列, 整体上来说相当于一个延迟队列,那么消费者在消费这个延迟队列的时候,消息的顺序必然不 会和生产者发送消息的顺序一致。
- 如果消息设置了优先级,那么消费者消费到的消息也必然不是顺序性的 。
解决方案
- 拆分多个 queue,每个 queue 一个 consumer,就是多一些 queue 而已,确实是麻烦点;或者就一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理。
- 如在消息体内添加全局有序标识(类似 Sequence ID) 来实现 。
提升数据可靠性有以下一些途径 :
- 设置 mandatory 参数或者备份交换器 (immediate 参数己被陶汰);
- 设置 publisher confirm机制或者事务机制;
- 设置交换器、队列和消息都为持久 化;
- 设置消费端对应的 autoAck 参数为 false 并在消费完消息之后再进行消息确认。