了解 RabbitMQ 3.4 的内存使用情况
“我的队列使用了多少内存?” 这是一个容易提出的问题,但回答起来就有点复杂了。 RabbitMQ 3.4 让您可以更清楚地了解队列如何使用内存。 这篇博文将对此进行一些探讨,并总体解释队列内存的使用。
一点背景知识
首先,我们需要了解 Erlang 如何管理内存。Erlang 与大多数垃圾回收语言有点不同,因为它没有全局堆。相反,每个进程都有一个单独的堆,该堆是其私有的。在 RabbitMQ 术语中,进程可能是队列、通道、连接等等。这意味着整个系统不必在每次需要进行垃圾回收时都停止运行;相反,每个进程都按照自己的计划收集垃圾。
这很好,但是当消息通过 RabbitMQ 时,它将通过几个不同的进程。我们希望避免在此过程中进行过多的复制。因此,Erlang 为我们提供了一种不同的内存管理方案,用于二进制文件,这些文件在 RabbitMQ 内部用于多种用途,其中最有趣的是消息体。二进制文件在进程之间共享并进行引用计数(引用由进程持有,并与所有其他内容一起进行垃圾回收)。
这如何应用于 RabbitMQ
这意味着消息体使用的内存在 RabbitMQ 中的进程之间共享。这种共享也发生在队列之间:如果一个交换器将消息路由到多个队列,则消息体在内存中仅存储一次。
因此,我们可以看到,询问“此队列使用了多少内存?” 是一个难以回答的问题 - 我们可以排除队列可能引用的任何二进制内存,从而导致低估,或者将其包括在内,可能导致高估。
早期版本的 RabbitMQ 没有尝试过多地处理这个难题;它们将队列的“内存使用量”报告为进程内存的大小(即不包括任何引用的二进制文件),并在全局内存细分中显示一个整体的“二进制内存使用量”。没有办法进一步调查。
RabbitMQ 3.4 为我们提供了一些更好的指导,无论是自上而下还是自下而上。首先,让我们看一下内存使用的自上而下的视图
与我们过去的情况相比,这里有一些不同之处。 总体内存使用细分现在有更多类别,并且有一个新的二进制内存“细分”。
我们单独显示二进制内存细分有两个原因;一个原因是计算起来可能非常昂贵(我们必须遍历服务器使用的所有内存;如果存在大量小二进制文件,这可能需要一段时间),另一个原因是我们不保证它会加起来达到总体内存细分中显示的大小(由于上面提到的二进制文件共享方式)。
但是我们可以在这里看到,几乎所有二进制文件的使用都是由于队列中的消息。此屏幕截图是从一个大部分静态的代理服务器上截取的,因此这正是我们所期望的。
但是队列呢?
好的,但是哪些队列正在使用所有这些内存? 我们可以通过查看每个队列的详细信息页面来调查这一点(当然,此信息也可以通过 rabbitmqctl
获得,但图片看起来更漂亮)
在这里我们可以看到 RabbitMQ 3.4 的另一个新功能:队列维护它包含的消息体字节总数的计数。因此我们看到,此队列包含 1.2GB 的消息体内容,其中 420MB 在内存中。我们可以假设这 420MB 全部位于队列使用的二进制内存中。该队列还使用了 421MB 的进程内存(纯粹是巧合,数量非常相似) - 这包括消息属性和标头,以及有关每条消息的元数据。
因此,可以合理地说“此队列正在使用 841MB 的内存” - 除非消息体也可能与其他队列共享。
顺便说一句,请注意,这里的“内存中”消息和“持久”消息不是反义词:非持久消息可能会在内存压力下分页到磁盘,而持久消息也可能在内存中。有关分页的更多信息,请参阅文档。
我们也可以在队列列表视图中看到此信息
(这里我单击了“+/-”链接以添加列来显示内存使用情况,并删除了一些其他列以提高清晰度。)
当然,这仍然无法完美地统计队列正在使用的内存量;在动态系统中,这样的事情可能是不可能的。但这使我们更加接近事实。