运行时调优
概述
RabbitMQ 运行在 Erlang 虚拟机 和运行时上。必须安装 兼容版本的 Erlang 才能运行 RabbitMQ。
Erlang 运行时包含 RabbitMQ 使用的许多组件。就本指南而言,最重要的组件是
- Erlang 虚拟机执行代码
epmd
将主机上的节点名称解析为 节点间通信端口
本指南将重点关注虚拟机。有关 epmd
的概述,请参阅 网络指南。
涵盖的主题包括
- 如何 为 RabbitMQ 节点配置 Erlang VM 设置
- 运行时调度程序,它们是什么,它们如何与 CPU 内核相关联,等等
- 运行时 线程活动指标:调度程序和 CPU 时间花在哪里
- 影响 CPU 利用率 的运行时特性
- 如何 降低负载较轻或较低的节点的 CPU 利用率
- 内存分配器 设置
- 打开的文件句柄限制
- 节点间通信缓冲区 大小
- Erlang 进程限制
- Erlang 崩溃转储
- 原子使用情况
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
和相关运行时标志 来关闭此推测性繁忙等待。
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
的别名)tnnps
nnts
nnps
ts
ps
s
ns
有关更详细的说明,请参阅 VM 标志文档。
降低 CPU 使用率
CPU 使用率本质上是一个与工作负载相关的指标。一些工作负载自然会使用更多 CPU 资源。其他工作负载会使用 诸如仲裁队列之类的磁盘密集型功能,如果磁盘 I/O 吞吐量不足,CPU 资源将被浪费,而节点将忙于等待 I/O 操作完成。
可以对“负载适中”的系统应用一些一般性建议,其中很大一部分或大多数连接和队列可能偶尔会闲置。换句话说,在本节中,我们将任何没有处于峰值容量附近的系统都视为“负载适中”。
这样的系统通常可以通过一些简单的步骤来降低其 CPU 使用率。这些建议可以显着降低某些工作负载的 CPU 使用率:例如,请考虑 此社区案例。
收集运行时线程统计信息
收集 运行时线程活动统计信息 数据,以了解调度程序和 CPU 时间是如何消耗的。这是做出明智决策的关键步骤。
关闭推测性调度程序繁忙等待
使用 +sbwt
和相关运行时标志 关闭推测性 调度程序繁忙等待
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"
该值以 MB 为单位。以下示例将预分配一个更大的 4 GB 区域
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+MMscs 4096"
要了解其他可用设置,请参阅分配器上的运行时文档。
打开文件句柄限制
大多数操作系统限制了可以同时打开的文件句柄数量。当 OS 进程(例如 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"
原子使用情况
与Erlang 进程限制类似,运行时对节点上可以存在的原子数量有限制。RabbitMQ 节点使用 500 万的默认值。此限制应该足以满足大多数用例。但是,在具有大量仲裁队列的环境中,可能需要增加限制。不推荐使用这种工作负载。
要增加限制,请使用 +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。