rabbitmq 学习 之lazy queues(30)

RabbitMQ从3.6.0版本开始引入了惰性队列(Lazy Queue)的概念。惰性队列会尽可能的将消息存入磁盘中,而在消费者消费到相应的消息时才会被加载到内存中,它的一个重要的设计目标是能够支持更长的队列,即支持更多的消息存储。当消费者由于各种各样的原因(比如消费者下线、宕机亦或者是由于维护而关闭等)而致使长时间内不能消费消息造成堆积时,惰性队列就很有必要了。

默认情况下,当生产者将消息发送到RabbitMQ的时候,队列中的消息会尽可能的存储在内存之中,这样可以更加快速的将消息发送给消费者。即使是持久化的消息,在被写入磁盘的同时也会在内存中驻留一份备份。当RabbitMQ需要释放内存的时候,会将内存中的消息换页至磁盘中,这个操作会耗费较长的时间,也会阻塞队列的操作,进而无法接收新的消息。虽然RabbitMQ的开发者们一直在升级相关的算法,但是效果始终不太理想,尤其是在消息量特别大的时候。

惰性队列会将接收到的消息直接存入文件系统中,而不管是持久化的或者是非持久化的,这样可以减少了内存的消耗,但是会增加I/O的使用,如果消息是持久化的,那么这样的I/O操作不可避免,惰性队列和持久化消息可谓是“最佳拍档”。注意如果惰性队列中存储的是非持久化的消息,内存的使用率会一直很稳定,但是重启之后消息一样会丢失。

 

Making a Queue Lazy

队列具备两种模式:default和lazy。默认的为default模式,在3.6.0之前的版本无需做任何变更。lazy模式即为惰性队列的模式,可以通过调用channel.queueDeclare方法的时候在参数中设置,也可以通过Policy的方式设置,如果一个队列同时使用这两种方式设置的话,那么Policy的方式具备更高的优先级。如果要通过声明的方式改变已有队列的模式的话,那么只能先删除队列,然后再重新声明一个新的。

在队列声明的时候可以通过“x-queue-mode”参数来设置队列的模式,取值为“default”和“lazy”。下面示例中演示了一个惰性队列的声明细节:

Map<String, Object> args = new HashMap<String, Object>();
args.put("x-queue-mode", "lazy");
channel.queueDeclare("myqueue", false, false, false, args);

对应的Policy设置方式为:

rabbitmqctl set_policy Lazy "^myqueue$" '{"queue-mode":"lazy"}' --apply-to queues

惰性队列和普通队列相比,只有很小的内存开销。这里很难对每种情况给出一个具体的数值,但是我们可以类比一下:当发送1千万条消息,每条消息的大小为1KB,并且此时没有任何的消费者,那么普通队列会消耗1.2GB的内存,而惰性队列只消耗1.5MB的内存。

据官网测试数据显示,对于普通队列,如果要发送1千万条消息,需要耗费801秒,平均发送速度约为13000条/秒。如果使用惰性队列,那么发送同样多的消息时,耗时是421秒,平均发送速度约为24000条/秒。出现性能偏差的原因是普通队列会由于内存不足而不得不将消息换页至磁盘。如果有消费者消费时,惰性队列会耗费将近40MB的空间来发送消息,对于一个消费者的情况,平均的消费速度约为14000条/秒。

Switching Queue Mode at Runtime

如果要将普通队列转变为惰性队列,那么我们需要忍受同样的性能损耗。当转变为惰性队列的时候,首先需要将缓存中的消息换页至磁盘中,然后才能接收新的消息。反之,当将一个惰性队列转变为普通队列的时候,和恢复一个队列执行同样的操作,会将磁盘中的消息批量的导入到内存中。

Caveats and Limitations

Lazy queues are appropriate when keeping node memory usage low is a priority and higher disk I/O and disk utilisation are acceptable. Lazy queues have other aspects that should be considered.

延迟队列适用于 优先考虑低内存使用,并且可以接受高磁盘io 适用的场景。

Node Startup

当节点正在运行并处于正常操作下时,惰性队列将把所有消息保存在磁盘上,惟一的例外是正在运行的消息。

当一个RabbitMQ节点启动时,所有队列(包括惰性队列)将最多向RAM加载16,384条消息。如果启用队列索引嵌入(queue_index_embed_msgs_below配置参数大于0),这些消息的有效负载也将加载到RAM中。

例如,一个包含20,000条消息(每个消息为4,000字节)的延迟队列将把16,384条消息加载到内存中。这些消息将使用63MB的系统内存。队列进程将使用另外8.4MB的系统内存,使总数略高于70MB。

如果RabbitMQ节点受到内存限制,或者节点上托管了许多延迟队列,那么这是容量规划的一个重要考虑因素。

重要的是要记住,内存或磁盘空间供应不足的RabbitMQ节点将无法启动。

将queue_index_embed_msgs_below设置为0将禁用队列索引中的有效负载嵌入。因此,延迟队列不会在节点启动时将消息有效负载加载到内存中。有关详细信息,请参阅持久性配置指南。

当将queue_index_embed_msgs_below设置为0时,所有消息都将存储到消息存储库中。由于许多消息跨多个延迟队列,这可能导致更高的磁盘使用率和更高的文件描述符使用率。

消息存储是面向追加的,并使用压缩机制回收磁盘空间。在极端情况下,它可以使用比存储在磁盘上的消息有效负载总和多两倍的磁盘空间。重要的是要提供过多的磁盘空间来解释这些峰值。

消息存储库中的所有消息都存储在称为段文件或段的16MB文件中。每个队列对于它必须访问的每个段文件都有自己的文件描述符。例如,如果100个队列存储10GB的消息,那么消息存储中将有640个文件,最多有64000个文件描述符。确保节点具有足够高的打开文件限制,并在有疑问时(例如,到300K或500K)进行过度配置。对于新安装,可以使用msg_store_file_size_limit配置键增加消息存储使用的文件大小。不要更改现有安装的段文件大小,因为这会导致节点忽略消息的子集,并可能破坏段文件压缩。

Lazy Queues with Mixed Message Sizes

If all messages in the first 10,000 messages are below the queue_index_embed_msgs_below value, and the rest are above this value, only the first 10,000 will be loaded into memory on node startup.

 

Mirroring of Lazy Queues

启用自动队列镜像时,请考虑所涉及的队列在磁盘上的预期数据集。具有大量数据集(例如,几十gb或更多)的队列必须将其复制到新添加的镜像,这可能会对集群资源(如网络带宽和磁盘I/O)造成很大的负载。例如,这是懒惰队列的常见场景。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值