RabbitMQ 内存管理
1. 存储机制
-
持久化的消息
在到达队列时就被写入磁盘,并且如果可以,持久化的消息也会在内存中保存一份备份,从而提高一定性能,当内存吃紧时再从内存换入磁盘。
-
非持久化的消息
一般只保存在内存中,当内存吃紧的时候会被换入磁盘
-
持久层:
逻辑上的概念,世纪包含两个部分:
-
队列索引(rabbit_queue_index)
负责维护队列中落盘消息的信息,包括消息的存储地点,是否已经被交付给消费者,是否已经被消费者ack等等。每个队列都有一个与之对应的rabbit_queue_index
-
消息存储(rabbit_msg_store)
以键值对的方式存储消息,被所有队列共享,每个节点只有一个。
rabbit_msg_store又可以分为,msg_store_persistent和msg_store_transient,其中msg_store_persistent负责持久化消息的持久化,重启后消息不会丢失。msg_store_transient负责非持久化消息的持久化,重启后消息会消失。
-
-
消息(包括消息体,属性,和headers)
可以存储在rabbit_queue_index中,也可以存储在rabbit_msg_store里。
可以根据queue_index_embed_msgs_below配置的大小,将小的消息存储在rabbit_queue_index中,大的消息存放在rabbit_msg_store中,默认的大小时4096B
-
队列的结构
通常队列由rabbit_amqqueue_process以及backing_queue两部分组成
-
rabbit_amqqueue_process负责协议相关的消息处理,即接收生产者发送的消息,向消费者交付消息,处理消息的确认(包括生产者的confirm和消费端的ack)等等。
-
backing_queue是消息存储的具体形式和引擎,并向rabbit_amqqueue_process提供相关接口以供调用
-
-
信息的传递过程
- 如果消息投递的目的队列是空的,并且由消费者订阅了这个队列,那么该消息会直接发送给消费者,不会经过队列这一步。
- 当消息无法直接传递给消费者时,需要暂时将消息存入队列。以便重新投递。
- 消息随着系统的负责,在队列中不断的流动,其状态也会不断发生变化,可能会有以下四种状态
- alpha:消息内容(包括消息体,属性和headers)和消息索引都存储在内存中
- beta: 消息内容存储在磁盘中,消息索引存储在内存中
- gamma: 消息内容保存在磁盘中,消息索引在磁盘和内存中都有
- delta:消息内容和索引都在磁盘中
- backing_queue的默认实现是rabbit_variable_queue,其内部通过5个子队列Q1,Q2,delta,Q3,Q4。其中Q1,Q4只包含alpha的内容,Q2,Q3包含beta和gama的内容。消息按照Q1->Q2->delta->Q3->Q4的顺序流动。
2. 惰性队列
-
从RabbitMQ 3.6.0起支持的特性,队列将每一条到来的消息保存在磁盘上,只有在消费者订阅时才将部分消息加载到内存。用于支持非常长的队列。
-
Lazy队列可以显著降低内存的占用,但由于每次入队和检索都会进行磁盘io操作,因此增加了io次数以及cpu的负担。
-
默认情况下,当消息发布到队列时会被保存在内存中,从而可以尽快的将消息传递给消费者。
-
当服务器认为需要释放内存时,内存中的消息会被换页到磁盘上。换页操作很耗费时间并且会阻塞队列。
-
创建Lazy Queue
-
通过queue.declare的arguments进行设置
通过设置键值对 “x-queue-mode” 使用"defualt"或"lazy"即可设定声明的队列是lazy队列还是常规队列
-
通过policy进行设定
rabbitmqctl set_policy Lazy "queueName" '{"queue-mode":"lazy"}' --apply-to queue $ rabbitmqctl set_policy Lazy "mqueue" --apply-to queues '{ "queue-mode":"lazy"}' Setting policy "Lazy" for pattern "mqueue" to "{ "queue-mode":"lazy"}" with priority "0" for vhost "/" ...
-
-
运行时变更QueueMode的影响
- 队列阻塞将当前的所有消息换页到磁盘中
- 完成后接收publish的消息以及其他信息。
3. 内存和磁盘警告
- RabbitMQ的消息会存储在内存中,当内存的时候超过配置的阈值或磁盘的空间低于配置的阈值时,RabbitMQ都会暂时阻塞客户端的连接(connection),以避免服务器的崩溃。同时客户端与服务器之间的心跳检测也会失效。
- 被阻塞的connection要么是blocking要么是blocked状态,如果该connection不试图向broker发送消息,则是blocking状态,其中的消费者也可以继续获取消息。如果试图向broker发送消息,则其处于blocked状态,该connection会被停止发送消息
- 一个集群中,如果一个broker的节点或者内存受限,都会引起整个集群中偶有的connection阻塞
1.内存告警
- RabbitMQ服务器在运行或者执行rabbitmqctl set_vm_memory_high_watermark命令时计算系统内存的大小并确定RabbitMQ可以占用的最大内存大小是多少。当使用的内存超过设定值时,就会产生内存告警,并阻塞所有生产者的满屏,一旦告警被解除(有消息被消费或被从内存存储转储到磁盘等情况的发生),一切都会恢复正常。
2. 磁盘告警
- 当剩余的磁盘空间低于确定的阈值时,RabbitMQ同样也会阻塞生产者,这样可以避免因非持久化的消息陈先生换页而耗尽磁盘空间导致服务器崩溃。默认情况下阈值为50M,即剩余磁盘空间小于50MB时会阻塞生产者并停止内存中的消息的换页操作。