说明
在之前的博文中我对RabbitMQ的一些扩展特性进行了翻译学习,本篇博文我将继续翻译学习RabbitMQ的消费者优先级(Consumer Priorities)和优先级队列(Priority Queues)相关内容。
正文
消费者优先级(Consumer Priorities)
消费者的优先级可以确保在不同优先级消费者都活跃消费消息时,高优先级的消费者会首先接收消息,只有在高优先级的消费者阻塞时,低优先级的消费者才会收到消息。
通常,活跃的消费者在连接到队列后,以循环(round-robin)的方式接收消息。但是当消费者的优先级被使用时,只有在所有活跃的消费者都处于相同优先级的情况下才会以循环的方式接收消息。
活跃的消费者
在上面提到了消费者的两种状态 ----- 活跃或者阻塞。在任何情况下,任意一个消费者只能是其中的一种状态。活跃的消费者是指该消费者不需要等待就可以直接接收消息,但是当消费者因为管道中未确定消息的数量达到了basic.qos方法设置的阈值,或者是因为网络阻塞而无法接收消息时,该消费者由活跃变为阻塞状态。
因此对于每个队列,至少会处于以下三种情况的一种:
- 队列没有活跃的消费者
- 队列是空的
- 队列正忙着向消费者传递消息
注意,消费者可能每秒都在活跃和阻塞状态中来回切换,我们不能通过管理插件或者rabbitmqctl命令来判断消费者的状态。
通常在使用消费者优先级时,我们都希望高优先级的消费者能接收全部的消息直到它变为阻塞状态,此时,低优先级的消费者能接着接收消息。所以理解RabbitMQ的发送消息原理就十分重要,在高优先级消费者阻塞时,RabbitMQ仍会优先传递消息,在仍存在活跃的低优先级的消费者时,它不会等待高优先级消费者的状态改变。
消费者优先级的使用
设置basic.consume方法的x-priority参数为一个整数。在没有设置该参数时,消费者默优先级为0,更大的数字表明更高的优先级,正负数都可以用来设置。
Channel channel = ...;
Consumer consumer = ...;
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-priority", 10);
channel.basicConsume("my-queue", false, args, consumer);
以上代码创建了一个优先级为10的消费者。
优先级队列(Priority Queue Support)
在RabbitMQ的3.5.0版本中实现了对优先级队列的支持,任何一个队列都可以通过客户端提供的可选参数(但是不同于其他的可选参数,该参数只能通过客户端进行设置,不能通过policies命令)设置一个优先级。这个优先级的数值最大可以在支持到255,但一般建议使用1-10。
客户端的可选参数
为声明一个优先级队列,可以对可选参数x-max-priority的值进行设置。该参数的值应该是一个正整数,范围为1-255,数值越大表示队列支持的优先级越高。
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.properites的priority字段值来发送优先级消息,数值越高表示优先级越大。
优先级的声明从设计上就不支持使用policies命令。
性能
在AMQP 0-9-1规范中对优先级的工作方式有些模糊,它指出所有的队列都必须至少支持优先级2,可能支持到10,但是它没有说明如何对待没有设置优先级的消息。
与AMQP 0-9-1规范相比,RabbitMQ的队列默认不支持优先级。当创建优先级队列时,开发者可以设置一个自认为比较合适的数值作为最大的优先级。当选择设置优先级数值时,一些事情必须被考虑。
每个队列的每个优先级别都会耗费内存和硬盘资源。当然也有额外的CPU消耗,尤其在消费的时候,所以你不能期望创建一个特别大的优先级别数值。
消息的优先级priority字段值是一个无符号的字节(unsigned byte),所以实际使用中值的范围应该是0到255。
当消息的优先级设为0时表示该消息没有优先级。当消息的优先级超过了设置的最大优先级时,消息将会被按照最大的优先级进行发送。
优先级的最大数值和资源的使用
如果希望使用优先级队列,我们推荐使用1-10的优先级值。当使用更高的优先级时会通过更多的Erlang进程消耗更多的CPU资源,同时运行调度(Runtime scheduling)也会受到影响。
对消费者的影响
理解消费者在消费优先级队列时如何工作是十分重要。默认情况下,消费者在确定消息前会被发送大量的消息,这仅会受到网络压力的影响。
所以,如果有大量的消费者连接到一个空队列,随之消息被生产者发送,那么这些消息到队列中可能不会停留,这时,优先级队列就无法对消息进行优先级排序。
对其他特性的影响
正常情况下,优先级队列拥有标准队列的所有特性:它们可以支持持久化,分页,镜像等特性。但是这里有两个影响需要开发人员注意:
那些需要过期的消息仍然只会在队列头部过期(丢弃或重新死信路由)。这意味着不像其他正常队列,甚至每个队列的TTL都会导致过期的低优先级消息被未过期的高优先级消息阻塞,这些消息将不会被发送,当仍会出现在队列的统计中。
当设置了队列的最大长度时,一般情况下,将会在队列头部丢弃消息来控制队列长度。这意味着可能会发生我们不希望的事情----- 高优先级的消息可能会被丢弃,为低优先级的消息让路。
为什么不支持Policy命令
队列设置可选参数值的最简便方法是使用policies命令,该方式被推荐用来设置TTL,队列长度和其他可选参数值。
然而,policies命令不能用来配置队列的优先级,这是因为policies命令是动态的,在队列被重新声明后是可以被改变的。但是优先级队列在被声明后是不希望改变优先级数值的,所以使用policies命令进行设置时不安全的。