进阶盘点Rabbitmq工具特性及常规处理方法

Posted 苏研大云人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进阶盘点Rabbitmq工具特性及常规处理方法相关的知识,希望对你有一定的参考价值。

点击关注哦


前言

目前Openstack产品,多数企业会选择Rabbitmq开源工具作为消息传递工具。我们的EC产品也是如此,但是会遇到很多相关的问题,比如消息集群突然down了,或者消息卡在某个队列导致命令失败等等。面对这些问题,我们通常的处理方法是重启消息集群,或者重启服务保证业务继续运行,但是深究原因,往往找不到准确的答案。


本文将从消息的本质特性出发,进行探究,并能够总结出原因及处理方法。

Rabbitmq消息队列的基础概念,简要提及一下,包括Exchange(消息路由)、Queue(队列)、Connection(连接)、Channel(信道)、Policy(队列策略)等,消息分为direct、topic、fanout三种。除了上述概念外,Rabbitmq有一个很有用的插件,rabbitmq_management,本文多数内容将借助这个插件进行一一阐述。这个插件启动的默认端口是15672,通过web应用展示。典型的,可以通过这个页面,查看到集群节点的状态,如下图一所示:



图一 节点状态


通过这个表格,显而易见,第二台节点down了,但是上面还有很多的数据,差距比较大,这又是什么含义呢?比如Socket descriptors列,第三台节点是0;Memory列,第一台节点的数据是1.6GB,这么大的内存空间究竟用在了什么地方?Disk space列的数据差异,又会有怎样的影响呢?这些疑问,将在本文逐个揭晓。

内存

首先,在内存的开销上,Rabbitmq以下简称MQ,它使用Erlang语言实现的,其每个队列都有独立的一套进程维护,包括队列、连接、信道等,并且具有独立的私有堆栈。MQ内存的管理模式是binaries,这些信息在多个进程之间共享,主要包含大量的查询、存储、维护工作。


上述图表中,第一台节点的内存使用1.6GB的空间,通过如下图二,我们可以进一步了解它的使用:



图二 内存空间描述


队列数据的开销是15MB,用于连接的读写数据量是大约20MB,用于信道的开销大约是7MB,mnesia作为MQ的存储数据库,开销大概在68MB,进程及系统的开销大约是63MB,剩下的空间,全部用于Binaries,大约是1.4GB,这表明这台节点的业务量还是比较繁忙的,包括大量的查询、存储开销,如队列索引查询,消息的置换开销(内存到磁盘、磁盘到内存)等。


 从队列的数量上来看,一个队列的开销在30KB左右,这个环境的队列数量是500多个,因此队列的开销基本稳定在15MB,主要的变化体现在连接开销上,消息都是通过连接的信道,传递到MQ上,部分消息会存储在内存上,其它消息会被置换到磁盘上,而当这个节点的连接负载逐步增大,那么Erlang处理进程也会逐步增加,同时文件句柄数也逐步增加。


再看图一,节点三上的socket连接数为0,因此,它的内存开销会小的多,并且开销主要用于队列的维护上,相应地Erlang进程、文件句柄数都会小很多。这个环境出现这种情况的原因是,客户端在配置MQ的时候,只配了第一台节点。


当然,内存也有保护机制,MQ通过vm_memory_high_watermark这个值进行配置,默认情况下是0.4,这个值代表内存使用不能超过系统可用的40%,否则会出现内存告警,并且会阻塞该节点上的所有连接,这就是在Openstack产品中,有时候会出现5672端口连接不上的一种原因。也可以配置内存使用的上限值,如果这个值超过系统的可用值,以系统可支配的内存值为准。内存达到了阈值,此时MQ broker会阻塞生产者,并采取相应的措施,它会将队列的内容,从内存置换到磁盘,从而缓解内存压力。而发出这个置换动作的命令,并不是要等到内存达到上限才开始执行,MQ的设计比较巧妙,默认情况下,当内存达到设定阈值一半的时候,就开始消息的置换动作。


这个值也可以通过如下参数进行配置,vm_memory_high_watermark_paging_ratio,可以调节到1,但这是很不和谐的做法,这将导致系统变的非常卡,甚至奔溃。如果,配置的非常小,这将导致频繁的磁盘置换动作,增大了系统的维护开销。因此,这个值最好不要随意变更。


磁盘

当内存开始置换的操作,如果出现如下情况:消息队列没有消费者,消息不停地往队列里写,通过上一节,消息会从内存中不停地置换到磁盘,磁盘也会被充满,MQ对磁盘也有保护机制,默认的预留上限值是50MB,超过这个值的时候,就会触发告警,同时阻塞消息从内存置换到磁盘,从而进一步阻塞内存,最后阻塞生产者。


这里,需要提一下消息的持久化类型,消息可以设置为持久化消息、非持久化消息,顾名思义,持久化消息需要存储到磁盘中去。但是,当遇到内存上限的时候,非持久化消息,也有可能被换出到磁盘中,如果磁盘空间设置的太小,MQ服务有可能会奔溃。MQ会定时监控磁盘的可用空间,默认的监控时间间隔是10s,当非常接近极限时,检测的间隔改为每秒10次。同样,可以通过修改disk_free_limit值进行配置。

如上文所述,消息在到达队列之时,一部分存储在内存中,一部分需要存储到磁盘中,这就引入了另外一个概念:持久化层。在MQ中,持久化层,是针对队列而言,包括队列索引和消息存储。队列索引,除了定位消息的位置,还会维护消息的交付、确认状态等;消息存储,是包含消息内容、属性、消息头的键值对。


一般持久化消息,在到达之时,就被写入到磁盘中,而非持久化消息当遇到内存压力的情况时,也会被写入磁盘。MQ中并不是如此机械地进行运作,为了提升效率,引入queue_index_embed_msgs_below参数,默认是4096,这个值表明当消息(包括属性信息)的长度小于这个值,会被存储到队列索引中去,从而提升存取速率,这样不需要二次运算,但是会消耗内存空间。


当内存压力情况下,持久化层会尽可能快地将消息置换到磁盘中,并从内存中移除。但是内存中,还会留存必要的内容,包括:没有确认的消息元数据。每个队列的索引需要在内存中存在至少一个段文件。而这个文件句柄,可以包括16384条消息的记录。因此,稍微调大这个值,内存的增长会非常快。在队列数量比较多的场景下,队列会不停地访问磁盘,因此会产生大量的文件句柄,此时文件句柄达到上限,服务会出现拥塞的情况。这些文件句柄主要用于队列,也有部分用于网络连接。


当访问磁盘的队列比较多的时候,这些文件句柄在队列之间是可以共享的,这就会出现一个文件句柄在1秒内被多次重复打开,如果文件句柄设的大一点,会有效的提升性能,如图一所示,文件句柄数才用了809,而系统的上限值是16384,这个值远远够用。


镜像队列

下面,本文的又一个重点内容:镜像队列。

默认情况下,集群的队列只会存在于一个声明它的节点上面;这和路由、绑定不同,它们会在所有节点存在(就是元数据的信息);如果给镜像加了镜像队列的设置,那么此时队列也会跨多个节点存在。


每个镜像队列,都会有主队列、多个镜像队列,如果主队列消失了,那么剩下的镜像队列,会按照时间顺序,选取最早创建的,作为新的主节点。消息发布到队列上,会被复制到所有的镜像队列,无论连接到哪个节点,消费者都与主队列连接,而镜像会删除在主服务器上被确认的消息;因此,队列镜像可以增强可用性,但不会在节点之间分配负载(所有参与的节点都执行所有的工作)。镜像队列的设置,通过如下命令,并且策略随时可以变更:

rabbitmqctl set_policy / .* .* .*。

当镜像队列的主队列失效了,处理流程也需要分情况。如果其它的slave节点和这个节点已经同步了,那么此时,会从新的镜像队列中,选出一个作为新的主镜像队列。但是,如果当前没有同步的镜像队列,那么此时,默认情况下,为了保证消息不丢失,所有的队列关闭连接状态,即不接收、不消耗消息,这种情况,在Openstack产品中,也经常出现,log显示队列not Found,最终导致服务启动不了。


MQ针对这种情况,也有有效的策略,通过ha-promote-on-shutdown参数进行调节,默认情况下,这个值是when-synced,即只有和主节点同步后,才有可能被选为新的主节点。当这个值设置为always,所有其它的镜像节点,与主节点不同步的情况下,消息丢失,并选取新的主节点;此时之前的主节点恢复到集群后,消息也全部丢失。


尽管会丢失消息,但是保证了服务的高可用性。在Openstack产品中,并不是所有的消息都要保证不丢失的,比如上报状态的消息,这些过程是周期性,前一次丢失了,并不会影响下一次的结果,因此这类队列,设置为always会更好;反之,对于创建虚机类的消息,这些消息直接影响结果的状态,就不能随意丢弃,这类消息设置为默认情况更加合适。设置的命令如下所示:

rabbitmqctl set_policy ha-all '^(?!amq\.).*' '{"ha-mode": "all", "ha-promote-on-shutdown":"always"}'

当队列出现了不一致的情况,默认情况下,MQ的配置参数是ha-sync-mode: manual,即需要手动同步,同步的过程,其它的队列会被阻塞;也可以设置为ha-sync-mode: automatic,自动同步,但是会阻塞队列。针对这个参数,配置为自动的好处是任何节点都有可能被选举为主节点,这样队列失效的概率较低,缺点是同步的开销,有时候会阻塞服务的运行。而手动同步,也需要考虑在业务较小的情况下执行。



最后,由于篇幅限制,MQ的另一个重要内容:分区,及常规问题的处理方法,见该文章系列二,尽请期待。

以上是关于进阶盘点Rabbitmq工具特性及常规处理方法的主要内容,如果未能解决你的问题,请参考以下文章

盘点PCB制板常规需求

21.egrep进阶及文本处理工具应用

Linux命令:sed常规用法盘点

#yyds干货盘点#JUnit5学习之八:综合进阶(终篇)

RabbitMQ进阶——RabbitMQ 重试机制,看这篇就够了

#yyds干货盘点#RabbitMQ示例2:工作队列