Skip to the content.

优先级队列的支持

RabbitMQ 在核心版本 3.5.0 中支持了优先级队列的实现。通过使用可选参数可以将任何的队列转成优先级队列(但是,与其他使用可选参数而不是策略的特性不同)。优先级属性是一个数字类型的值,最大为 255;推荐在 1-10 之间。

使用客户端可选参数

为了申明一个优先级队列,在可选队列参数上使用 x-max-priority。这个参数值应该是个 1-255 之间的正整数,表示支持的最大优先队列级别。例如 Java 客户端代码如下:

Channel ch = ...;
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-max-priority", 10);
ch.queueDeclare("my-priority-queue", true, false, false, args);

然后发布者会通过 basic.properties 的字段 priority 发布具有优先的消息。数字越大表优先级越高。

使用策略申明优先级,从设计上是不支持的。

行为

关于优先级是怎样工作的 AMQP 0-9-1 规格上有点模糊(vague)。上面说所有的队列必须都支持 2 的优先级,也能支持升到 10。它没有定义在没有设置优先级的消息是如何处理的。

与 AMQP 0-9-1 不同的是,RabbhitMQ 队列默认是不支持优先级的。当创建优先级队列时,最大的优先级值视开发者选择而定。当选择这个值时,有一些事情需要考虑。

在每个队列上的每个优先级会有一些在内存和磁盘上的开销。这里也会对 CPU 有额外的消耗,特别是当消费的时候,所以你可能不喜欢创建一个优先级很高的队列。

消息的 priority 是一个未分配的 byte 类型的字段,所以在实战中应该设置为 0-255。

如果这个字段的值为 0,即视作没有设置优先级的消息。优先级高于队列最大值的消息被视为以最大优先级发布的消息。

最大优先级和资源使用

如果要设置优先级队列,那么我们建议设置在 1-10 之间。目前使用优先级越高,那么 RabbitMQ 宿主 Erlang 进程消耗的 CPU 资源就越高。运行时调度将会收到影响。

与消费者交互

理解使用优先级队列如何工作是非常关键的。默认情况下,在消费者确认之前会发送大量的消息,这只受制于网络背压(network back-pressure)。

背压这个属于其实从流体力学上借鉴过来的,关于背压解释详见 wiki 以及 https://medium.com/@jayphelps/backpressure-explained-the-flow-of-data-through-software-2350b3e77ce7

如果一个饥饿的消费者连接一个空队列,消息随后就会发布进来,那么这个消息就不会在这个队列等待任何时间。这个时候优先级队列是不会有任何机会来优先它们的。

在大多数时候,消费者在手动确认模式中使用 basic.qos,来限制消息的数量,这样就限制发出的消息数量,因此消息就能设置优先级了。

与其它功能交互

通常优先级队列有着标准 RabbitMQ 正常队列的所有功能:支持持久化,分页,镜像等。这里也有一些交互操作需要开发者应该注意的。

过期消息仍只作用在队列的头部。意思就是不像普通的队列,每个队列中 TTL 消息的优先级低的消息都落后于未过期的高优先级的。这些消息将永远不会被推送,但是它们仍然会存在队列中。

设置了队列的最大容量,一般到达这个阈值就会丢弃队列的头部消息。这也就意味者高优先级的消息可能会被丢弃,以便为低优先级的消息让路,这可能与你预期的结果不同。

为什么不基于策略定义优先级

在约定上,大多数队列的可选参数定义的通过策略也都能定义。推荐策略定义的有 TTL,队列限制容量以及其它可选队列参数。

但是策略不能用来配置优先级,因为在队列申明之后策略能够动态变化。优先级队列的优先级数字在队列申明后是不能变的,所以基于策略定义就不安全。

参考资料