跳至主要内容

理解 RabbitMQ 3.4 中的内存使用

·阅读 4 分钟
Simon MacMullen

"我的队列使用了多少内存?" 这是一个容易提出的问题,但要回答却稍微复杂一些。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 的内存” - 除了消息体也可能与其他队列共享之外。

顺便说一句,请注意“内存中”和“持久性”消息在这里不是反义词:非持久性消息可以在内存压力下被换出,持久性消息也可以在内存中。有关分页的更多信息,请参阅文档

我们也可以在队列列表视图中看到此信息

(在这里,我点击了“+/-”链接以添加列以显示内存使用情况并删除其他一些列以提高清晰度。)

当然,这仍然不能完美地计算队列使用了多少内存;在动态系统中,这可能是不可能的。但它使我们更接近目标。

© 2024 RabbitMQ. All rights reserved.