运行时调优
概述
RabbitMQ 运行在 Erlang 虚拟机 和运行时之上。要运行 RabbitMQ,必须安装 兼容版本的 Erlang。
Erlang 运行时包含 RabbitMQ 使用的许多组件。就本指南而言,最重要的组件是:
- Erlang 虚拟机执行代码
epmd将主机上的节点名称解析为 节点间通信端口
本指南将重点介绍虚拟机。有关 epmd 的概述,请参阅 网络指南。
涵盖的主题包括:
- 如何为 RabbitMQ 节点 配置 Erlang VM 设置
- 运行时调度器、它们的含义、与 CPU 核心的关系等。
- 运行时 线程活动指标:调度器和 CPU 时间花费在哪里。
- 影响 CPU 利用率 的运行时功能。
- 如何在负载适中或较低的节点上 降低 CPU 利用率。
- 内存分配器 设置。
- 打开文件句柄限制。
- 节点间通信缓冲区 大小。
- Erlang 进程限制。
- Erlang 崩溃转储。
- Atom 使用量。
VM 设置
Erlang VM 提供了广泛的 可配置选项,涵盖进程调度器设置、内存分配、垃圾回收、I/O 等。调整这些标志可以显著改变节点的运行时行为。
配置标志
大多数设置可以通过 环境变量 配置。一些设置有专用的变量,其他设置只能通过以下通用变量更改,这些变量控制 RabbitMQ 启动脚本传递给 Erlang 虚拟机的标志。
通用变量是:
RABBITMQ_SERVER_ERL_ARGS允许覆盖所有 VM 标志,包括 RabbitMQ 脚本设置的默认值。RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS允许将一组标志附加到 RabbitMQ 脚本设置的默认值。RABBITMQ_CTL_ERL_ARGS控制 CLI 工具 的 VM 标志。
在大多数情况下,推荐使用 RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS。它可以安全地覆盖默认值。例如,如果 RABBITMQ_SERVER_ERL_ARGS 中省略了一个重要标志,可能会无意中影响运行时性能特性或系统限制。
与其他由 RabbitMQ 使用的环境变量一样,RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS 及其同类项可以通过 单独的环境变量文件 设置。
CPU 利用率
CPU 利用率是一个与工作负载相关的特定主题。总的来说,当工作负载涉及的队列、连接和通道数量超过 CPU 核心数量时,所有核心都会被使用,无需任何配置。
运行时提供了几个控制核心使用方式的功能。
运行时调度器
运行时中的调度器将工作分配给执行它的内核线程。它们执行代码、执行 I/O、执行定时器等。调度器有许多设置可以影响整体系统性能、CPU 利用率、延迟和其他节点的运行时特性。
默认情况下,运行时会为检测到的每个 CPU 核心启动一个调度器。从 Erlang 23 开始,考虑了容器化环境(如 Docker 和 Kubernetes)中的 CPU 配额。
可以使用 +S 标志显式设置调度器数量。以下示例配置节点启动 4 个调度器,即使检测到更多可用核心。
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+S 4:4"
大多数情况下,默认行为效果良好。在共享或 CPU 受限的环境(包括容器化环境)中,显式配置调度器数量可能非常必要。
CPU 资源争用
运行时假定它不与其他工具或租户共享 CPU 资源。当发生这种情况时,使用的调度机制可能会非常低效,并导致某些操作的延迟显著(高达几个数量级)增加。
这意味着在大多数情况下,将 RabbitMQ 节点与其他工具共置或应用 CPU 时间切片是极不推荐的,并且会导致性能不佳。
调度器忙等待
当调度器没有更多工作可执行时,运行时可以将它们置于睡眠状态。将它们重新上线需要一定的成本,因此对于某些工作负载,不这样做可能是有益的。
这可以与一家拥有多条传送带的工厂进行比较。当一条传送带上的物品用完时,可以停止它。但是,一旦有了更多工作要做,重新启动它将花费时间。或者,传送带可以推测性地运行一段时间。
默认情况下,RabbitMQ 节点配置运行时调度器以禁用推测性等待。这有利于其中连接、通道、会话、队列或流副本可能存在不活动期的工作负载。
推测性忙等待是通过 +sbwt 和相关的运行时标志 控制的。
# This is the default used by modern RabbitMQ versions: it disables speculative
# busy waiting.
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+sbwt none +sbwtdcpu none +sbwtdio none"
禁用推测性等待可降低 CPU 资源有限或可突发使用的系统上的 CPU 使用率。
为了确定调度器在忙等待上花费了多少时间,请参阅 线程活动指标。忙等待通常在 top 和 pidstat 等工具的输出中被计为系统时间。
调度器到 CPU 核心绑定
调度器数量不一定与可用的 CPU 核心数量匹配,CPU 核心数量也不一定与硬件线程数量相关(例如,由于超线程)。因此,运行时必须决定如何将调度器绑定到硬件线程、CPU 核心和 NUMA 节点。
有几种可用的绑定策略。可以通过 RABBITMQ_SCHEDULER_BIND_TYPE 环境变量或 +stbt 运行时标志 的值来指定所需的策略。
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+stbt nnts"
RABBITMQ_SCHEDULER_BIND_TYPE="nnts"
请注意,仅当运行时能够检测到给定环境中的 CPU 拓扑时,该策略才会生效。
有效值为:
db(默认使用,在当前 Erlang 版本系列中是tnnps的别名)tnnpsnntsnnpstspssns
有关更详细的说明,请参阅 VM 标志文档。
降低 CPU 使用率
CPU 使用率从定义上来说是一个高度依赖工作负载的指标。某些工作负载自然会使用更多的 CPU 资源。其他工作负载使用 类似 quorum queues 的磁盘密集型功能,如果磁盘 I/O 吞吐量不足,节点在等待 I/O 操作完成时会浪费 CPU 资源。
对于“中等负载”系统,其中大部分连接和队列会不时地处于空闲状态,可以应用一些通用的建议。换句话说,在本节中,我们将任何未接近其峰值容量的系统视为“中等负载”。
这样的系统通常可以通过几个简单的步骤来减少其 CPU 占用。这些建议可以显著降低某些工作负载的 CPU 占用:例如,请参考 此社区案例。
收集运行时线程统计信息
收集 运行时线程活动统计 数据,以了解调度器和 CPU 时间是如何花费的。这是做出明智决策的关键一步。
关闭推测性调度器忙等待
如果推测性 调度器忙等待 已自定义,请使用 +sbwt 和相关的运行时标志 将其禁用。
# This is the default used by modern RabbitMQ versions: it disables speculative
# busy waiting.
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+sbwt none +sbwtdcpu none +sbwtdio none"
降低统计信息发射频率(增加统计信息发射间隔)
将 统计信息发射间隔 从默认的 5 秒增加到 15 或 30 秒。这将减少所有连接、通道和队列进行的周期性活动,即使它们在客户端操作方面是空闲的。对于大多数监视工具,监视频率 就足够了,甚至是最优的。
线程统计信息
RabbitMQ CLI 工具提供了一些 指标,有助于理解运行时线程活动。
rabbitmq-diagnostics runtime_thread_stats
是生成各种线程时间花费情况明细的命令。
该命令的输出将生成一个按线程活动百分比划分的表格:
emulator:通用代码执行。port:外部 I/O 活动(套接字 I/O、文件 I/O、子进程)。gc:执行垃圾回收。check_io:检查 I/O 事件。other,aux:忙等待、管理定时器、所有其他任务。sleep:休眠(空闲状态)。
外部 I/O 状态活动百分比过高可能表明节点和/或客户端的网络链路容量已满。这可以通过 基础架构指标 来确认。
休眠状态活动百分比过高可能表明节点负载较低或运行时调度器配置不适合可用硬件和工作负载。
内存分配器设置
运行时管理(分配和释放)内存。运行时内存管理是一个复杂的主题,有 许多可调参数。本节仅涵盖基础知识。
内存从称为载体的较大预分配区域中分配块。控制载体大小、块大小、内存分配策略等的设置通常称为分配器设置。
根据使用的分配器设置和工作负载,RabbitMQ 可能会经历不同程度的 内存碎片。找到最适合您工作负载的设置需要反复试验、测量(收集指标)和错误。请注意,一定程度的碎片是不可避免的。
以下是默认使用的分配器参数:
RABBITMQ_DEFAULT_ALLOC_ARGS="+MBas ageffcbf +MHas ageffcbf +MBlmbcs 512 +MHlmbcs 512 +MMmcs 30"
不要覆盖 RABBITMQ_DEFAULT_ALLOC_ARGS,而是将需要覆盖的标志添加到 RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS。它们将优先于默认值。因此,使用以下 RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS 值启动的节点
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+MHlmbcs 8192"
将使用以下有效的分配器设置:
"+MBas ageffcbf +MHas ageffcbf +MBlmbcs 512 +MHlmbcs 8192 +MMmcs 30"
对于某些工作负载,更大的预分配区域可以减少分配速率和内存碎片。要将节点配置为使用 1 GB 的预分配区域,请使用 RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS 将 +MMscs 1024 添加到 VM 启动参数中。
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+MMscs 1024"
以下示例将预分配一个更大的 4 GB 区域:
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+MMscs 4096"
要了解其他可用设置,请参阅 运行时关于分配器的文档。
打开文件句柄限制
大多数操作系统限制了可以同时打开的文件句柄数量。当操作系统进程(如 RabbitMQ 的 Erlang VM)达到限制时,它将无法打开新文件或接受更多 TCP 连接。
此限制在 网络指南 中有详细介绍。请注意,它无法通过 Erlang VM 标志进行配置。
节点间通信缓冲区大小
节点对之间的节点间流量使用一个具有缓冲区(称为节点间通信缓冲区)的 TCP 连接。默认大小为 128 MB。这对于大多数工作负载来说是一个合理的默认值。在某些环境中,节点间流量可能非常大,并达到缓冲区的容量。其他不适合默认值的工作负载包括传输非常大的(例如,几百兆字节)且不适合缓冲区的消息。
在这种情况下,可以使用 RABBITMQ_DISTRIBUTION_BUFFER_SIZE 环境变量或 +zdbbl VM 标志 来增加该值。该值以千字节为单位。
RABBITMQ_DISTRIBUTION_BUFFER_SIZE=192000
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+zdbbl 192000"
当缓冲区接近满容量时,节点将 记录 一条警告,提及繁忙的分布式端口(busy_dist_port)。
2019-04-06 22:48:19.031 [warning] <0.242.0> rabbit_sysmon_handler busy_dist_port <0.1401.0>
增加缓冲区大小可能有助于提高吞吐量和/或降低延迟。
Erlang 进程限制
运行时对节点上可以存在的 Erlang 进程(“轻量级线程”)数量有限制。默认值约为 100 万。在大多数环境中,这足以满足广泛的安全裕度。
具有特别 大量并发连接 或大量队列(例如,数十万)的环境可能需要调整此限制。这通过 RABBITMQ_MAX_NUMBER_OF_PROCESSES 环境变量实现,这是一个设置 +P Erlang VM 标志的便捷方法。
RABBITMQ_MAX_NUMBER_OF_PROCESSES=2000000
要直接设置该标志,请使用 RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS 环境变量。
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+P 2000000"
Atom 使用量
与 Erlang 进程限制 类似,运行时对节点上可以存在的 Atom 数量有限制。RabbitMQ 节点使用默认的 500 万。此限制应足以满足大多数用例。但是,在具有大量 quorum queues 的环境中,可能需要提高此限制。不推荐此类工作负载 。
要增加限制,请使用 +t 运行时参数。
# sets the limit to 9M
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+t 9000000"
或 RABBITMQ_MAX_NUMBER_OF_ATOMS 环境变量。
# sets the limit to 9M
RABBITMQ_MAX_NUMBER_OF_ATOMS=9000000
值必须是 10 的幂。
Erlang 崩溃转储
当运行时异常终止或收到 SIGUSER1 信号时,它将生成一个本地文件,称为 崩溃转储。
该文件包含终止时的一些运行时信息。此文件对于调试某些类型的问题可能很有用。在内存占用大的节点上,它也可能变得非常大。
要禁用崩溃转储文件,请将 ERL_CRASH_DUMP_BYTES 环境变量 设置为 0。