网络和 RabbitMQ
概述
客户端通过网络与 RabbitMQ 通信。代理支持的所有协议均基于 TCP。RabbitMQ 和操作系统都提供许多可调整的选项。其中一些与 TCP 和 IP 操作直接相关,另一些则与 TLS 等应用程序级协议有关。本指南涵盖了与 RabbitMQ 上下文相关的多个网络主题。本指南并非旨在成为详尽的参考,而只是一个概述。讨论的一些可调参数是特定于操作系统的。在涵盖特定于操作系统的主题时,本指南重点关注 Linux,因为它是 RabbitMQ 部署的最常见平台。
有几个区域可以进行配置或调整。每个区域在本指南中都有一个部分
- 接口 节点监听客户端连接的接口
- IP 版本首选项:双栈、仅 IPv6 和 仅 IPv4
- 端口 客户端使用、集群中的节点间流量 以及CLI 工具 使用的端口
- IPv6 支持 用于节点间流量
- TLS 用于客户端连接
- 调整以适应大量并发连接
- 高连接震荡 场景和资源耗尽
- TCP 缓冲区大小(影响吞吐量 和每个连接使用多少内存)
- 主机名解析 相关主题,例如反向 DNS 查询
- epmd 使用的接口和端口
- 如何暂停和恢复侦听器 以暂时停止和恢复新的客户端连接
- 其他 TCP 套接字设置
- 代理协议 支持客户端连接
- 内核 TCP 设置和限制(例如TCP 保活 和打开文件句柄限制)
- 当启用MacOS 应用程序防火墙 时,如何允许 Erlang 运行时接受入站连接
- 操作系统级调优 与网络相关
本指南还涵盖了一些与网络密切相关的主题
除了操作系统内核参数和 DNS 之外,所有 RabbitMQ 设置均通过 RabbitMQ 配置文件进行配置。
网络是一个广泛的主题。许多配置选项会对某些工作负载产生正面或负面影响。因此,本指南并非试图成为完整的参考,而是提供关键可调参数的索引并作为起点。
此外,本指南还涉及一些与网络密切相关的主题,例如
- 主机名、主机名解析和 DNS
- 连接生命周期日志记录
- 心跳(又名保活)
- 代理和负载均衡器
VMware Tanzu RabbitMQ 商业产品提供集群内压缩 功能。之前的文档链接指向 Tanzu RabbitMQ for Kubernetes 商业产品。
在单独的指南中介绍了网络相关问题的故障排除方法。
客户端连接的网络接口
为了让 RabbitMQ 接受客户端连接,它需要绑定到一个或多个接口并在(特定于协议的)端口上侦听。在 RabbitMQ 的术语中,这样的接口/端口对称为侦听器。侦听器使用 listeners.tcp.*
配置选项进行配置。
TCP 侦听器同时配置接口和端口。以下示例演示如何配置 AMQP 0-9-1 和 AMQP 1.0 侦听器以使用特定的 IP 和标准端口
listeners.tcp.1 = 192.168.1.99:5672
默认情况下,RabbitMQ 将在端口 5672 上的**所有可用接口**上侦听。可以将客户端连接限制为接口的子集,甚至仅限一个接口,例如,仅限 IPv6 接口。以下几个部分演示了如何执行此操作。
在双栈(IPv4 和 IPv6)接口上侦听
以下示例演示如何配置 RabbitMQ 以仅在 localhost 上侦听 IPv4 和 IPv6。
listeners.tcp.1 = 127.0.0.1:5672
listeners.tcp.2 = ::1:5672
对于现代 Linux 内核和 Windows 版本,当指定端口并且 RabbitMQ 配置为侦听所有 IPv6 地址但未明确停用 IPv4 时,将包含 IPv4 地址,因此
listeners.tcp.1 = :::5672
等效于
listeners.tcp.1 = 0.0.0.0:5672
listeners.tcp.2 = :::5672
仅在 IPv6 接口上侦听
在此示例中,RabbitMQ 将仅在 IPv6 接口上侦听。
listeners.tcp.1 = fe80::2acf:e9ff:fe17:f97b:5672
在仅 IPv6 的环境中,节点还必须配置为将 IPv6 用于节点间通信和 CLI 工具连接。
仅在 IPv4 接口上侦听
在此示例中,RabbitMQ 将仅在指定 IP 地址的 IPv4 接口上侦听。
listeners.tcp.1 = 192.168.1.99:5672 # Plain AMQP
listeners.ssl.1 = 192.168.1.99:5671 # TLS (AMQPS)
可以通过停用所有常规 TCP 侦听器来停用非 TLS 连接。只有启用 TLS 的 客户端才能连接。
# deactivates non-TLS listeners, only TLS-enabled (activated) clients will be able to connect
listeners.tcp = none
listeners.ssl.default = 5671
ssl_options.cacertfile = /path/to/ca_certificate.pem
ssl_options.certfile = /path/to/server_certificate.pem
ssl_options.keyfile = /path/to/server_key.pem
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = false
端口访问
RabbitMQ 节点绑定到端口(打开服务器 TCP 套接字)以接受客户端和 CLI 工具连接。其他进程和工具(如 SELinux)可能会阻止 RabbitMQ 绑定到端口。发生这种情况时,节点将无法启动。
CLI 工具、客户端库和 RabbitMQ 节点也会打开连接(客户端 TCP 套接字)。防火墙可以阻止节点和 CLI 工具相互通信。确保以下端口可访问
- 4369:epmd,RabbitMQ 节点和 CLI 工具使用的对等发现服务
- 5672、5671:AMQP 0-9-1 和 AMQP 1.0 客户端(分别不使用和使用 TLS)使用
- 5552、5551:RabbitMQ Stream 协议 客户端(分别不使用和使用 TLS)使用
- 6000 到 6500:用于流 复制
- 25672:用于节点间和 CLI 工具通信(Erlang 分布式服务器端口),并从动态范围分配(默认情况下限制为单个端口,计算为 AMQP 端口 + 20000)。除非确实需要这些端口上的外部连接(例如,集群使用联合 或在子网外部使用 CLI 工具),否则不应公开这些端口。
- 35672-35682:此客户端 TCP 端口范围供 CLI 工具用于与节点通信。默认情况下,该范围计算为
(服务器分布式端口 + 10000)
到(服务器分布式端口 + 10010)
- 15672、15671:HTTP API 客户端、管理 UI 和rabbitmqadmin(分别不使用和使用 TLS)(仅当启用管理插件 时)
- 61613、61614:STOMP 客户端(分别不使用和使用 TLS)(仅当启用STOMP 插件 时)
- 1883、8883:MQTT 客户端(分别不使用和使用 TLS),如果启用了MQTT 插件
- 15674:STOMP-over-WebSockets 客户端(仅当启用Web STOMP 插件 时)
- 15675:MQTT-over-WebSockets 客户端(仅当启用Web MQTT 插件 时)
- 15692、15691:Prometheus 指标(分别不使用和使用 TLS)(仅当启用Prometheus 插件 时)
可以配置 RabbitMQ 以使用不同的端口和特定的网络接口。
如何暂时停止新的客户端连接
可以暂停客户端连接侦听器以阻止接受新的客户端连接。现有连接不会受到任何影响。
这在节点操作期间非常有用,并且是将节点置于维护模式时执行的步骤之一。
要暂停节点上的所有侦听器并阻止新的客户端连接到它,请使用 rabbitmqctl suspend_listeners
rabbitmqctl suspend_listeners
与所有其他 CLI 命令一样,可以使用 -n
开关针对任意节点(包括远程节点)调用此命令。
# suspends listeners on node rabbit@node2.cluster.rabbitmq.svc: it won't accept any new client connections
rabbitmqctl suspend_listeners -n rabbit@node2.cluster.rabbitmq.svc
要恢复节点上的所有侦听器并使其再次接受新的客户端连接,请使用 rabbitmqctl resume_listeners
rabbitmqctl resume_listeners
# resumes listeners on node rabbit@node2.cluster.rabbitmq.svc: it will accept new client connections again
rabbitmqctl resume_listeners -n rabbit@node2.cluster.rabbitmq.svc
这两个操作都将在节点的日志中留下日志条目。
EPMD 和节点间通信
什么是 EPMD 以及如何使用它?
epmd(Erlang 端口映射守护程序)是一个小型附加守护程序,与每个 RabbitMQ 节点一起运行,并由运行时 用于发现特定节点侦听节点间通信的端口。然后,对等节点和CLI 工具 将使用该端口。
当节点或 CLI 工具需要联系节点 rabbit@hostname2
时,它将执行以下操作
- 使用标准操作系统解析器或inetrc 文件 中指定的自定义解析器将
hostname2
解析为 IPv4 或 IPv6 地址 - 使用上述地址联系在
hostname2
上运行的epmd
- 询问
epmd
节点rabbit
在其上使用的端口 - 使用解析的 IP 地址和发现的端口连接到节点
- 继续通信
EPMD 接口
默认情况下,epmd
将监听所有接口。可以使用 ERL_EPMD_ADDRESS
环境变量将其限制到特定数量的接口。
# makes epmd listen on loopback IPv6 and IPv4 interfaces
export ERL_EPMD_ADDRESS="::1"
当更改 ERL_EPMD_ADDRESS
时,必须停止主机上的 RabbitMQ 节点和 epmd
。对于 epmd
,请使用
# Stops local epmd process.
# Use after shutting down RabbitMQ.
epmd -kill
终止它。服务将在启动时由本地 RabbitMQ 节点自动启动。
环回接口将隐式添加到该列表中(换句话说,epmd
将始终绑定到环回接口)。
EPMD 端口
默认的 epmd 端口是 4369,但可以使用 ERL_EPMD_PORT
环境变量更改它。
# makes epmd bind to port 4369
export ERL_EPMD_PORT="4369"
集群中的所有主机集群必须使用相同的端口。
当更改 ERL_EPMD_PORT
时,必须停止主机上的 RabbitMQ 节点和 epmd
。对于 epmd
,请使用
# Stops local epmd process.
# Use after shutting down RabbitMQ.
epmd -kill
终止它。服务将在启动时由本地 RabbitMQ 节点自动启动。
节点间通信端口范围
RabbitMQ 节点将使用某个范围内的端口,该范围称为节点间通信端口范围。CLI 工具在需要联系节点时也会使用相同的端口。该范围可以修改。
RabbitMQ 节点使用称为“分发端口”的端口与 CLI 工具和其他节点通信。它从一系列值中动态分配。对于 RabbitMQ,默认范围限制为一个计算值为 RABBITMQ_NODE_PORT
(AMQP 0-9-1 和 AMQP 1.0 端口)+ 20000 的单个值,这导致使用端口 25672。可以使用 RABBITMQ_DIST_PORT
环境变量配置此单个端口。配置
配置防火墙规则时,必须允许来自每个集群节点的 IP 地址和可能使用 CLI 工具的每个主机的节点间通信端口上的远程连接。
RabbitMQ 命令行工具也使用一系列端口。默认范围是通过获取 RabbitMQ 分发端口值并向其添加 10000 来计算的。接下来的 10 个端口也属于此范围。因此,默认情况下,此范围为 35672 至 35682。可以使用 RABBITMQ_CTL_DIST_PORT_MIN
和 RABBITMQ_CTL_DIST_PORT_MAX
环境变量配置此范围。请注意,将范围限制为单个端口将阻止多个 CLI 工具在同一主机上同时运行,并可能影响需要并行连接到多个集群节点的 CLI 命令。因此,建议使用 10 个端口的范围。
配置防火墙规则时,必须允许来自每个集群节点的 IP 地址和可能使用 CLI 工具的每个主机的节点间通信端口上的远程连接。CLI 工具和集群正常工作需要打开 epmd 端口。
在 Windows 上,当 RabbitMQ 作为服务运行时,以下设置无效。有关详细信息,请参阅Windows 配置。
RabbitMQ 使用的范围也可以通过 rabbitmq.conf
中的两个配置键控制
inet_dist_listen_min
inet_dist_listen_max
它们定义了范围的下限和上限(包含)。
以下示例使用具有单个端口但与默认值不同的范围
inet_dist_listen_min = 33672
inet_dist_listen_max = 33672
要验证节点用于节点间和 CLI 工具通信的端口,请运行
epmd -names
在该节点的主机上。它将生成如下所示的输出
epmd: up and running on port 4369 with data:
name rabbit at port 25672
节点间通信缓冲区大小限制
节点间连接使用缓冲区来存储待发送的数据。当缓冲区达到最大允许容量时,将对节点间流量应用临时限流。该限制通过 RABBITMQ_DISTRIBUTION_BUFFER_SIZE
环境变量(以千字节为单位)控制。默认值为 128 MB(128000
kB)。
在节点间流量较大的集群中,增加此值可能会对吞吐量产生积极影响。不建议使用低于 64 MB 的值。
使用 IPv6 进行节点间通信(以及 CLI 工具)
除了客户端连接的独占 IPv6 使用用于客户端连接外,还可以将节点配置为独占使用 IPv6 进行节点间和 CLI 工具连接。
这涉及到几个地方的配置
可以使用 IPv6 进行节点间和 CLI 工具通信,但使用 IPv4 进行客户端连接,反之亦然。此类配置可能难以排查和推理,因此建议在整个过程中使用相同的 IP 版本(例如 IPv6)或双栈设置。
节点间通信协议
要指示运行时使用 IPv6 进行节点间通信和相关任务,请使用 RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS
环境变量传递几个标志
# these flags will be used by RabbitMQ nodes
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="-kernel inetrc '/etc/rabbitmq/erl_inetrc' -proto_dist inet6_tcp"
# these flags will be used by CLI tools
RABBITMQ_CTL_ERL_ARGS="-proto_dist inet6_tcp"
上面的 RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS
使用两个密切相关的标志
-kernel inetrc
用于配置inetrc 文件的路径,该文件控制主机名解析-proto_dist inet6_tcp
用于指示节点在连接到对等节点和侦听 CLI 工具连接时使用 IPv6
/etc/rabbitmq/erl_inetrc
中的 erl_inetrc
文件将控制主机名解析设置。对于仅 IPv6 的环境,它必须包含以下行
%% Tells DNS client on RabbitMQ nodes and CLI tools to resolve hostnames to IPv6 addresses.
%% The trailing dot is not optional.
{inet6,true}.
CLI 工具
对于 CLI 工具,请使用与上面用于 RabbitMQ 节点的相同运行时标志,但使用不同的环境变量 RABBITMQ_CTL_ERL_ARGS
提供它。
RABBITMQ_CTL_ERL_ARGS="-proto_dist inet6_tcp"
请注意,一旦指示使用 IPv6,CLI 工具将无法连接到不使用 IPv6 进行节点间通信的节点。这涉及到与目标 RabbitMQ 节点位于同一主机上的 epmd
服务。
epmd
epmd
是一个小型辅助守护进程,它与 RabbitMQ 节点一起运行,并允许其对等节点和 CLI 工具发现它们应使用哪个端口与其通信。它可以配置为绑定到特定接口,就像 RabbitMQ 侦听器一样。这是使用 ERL_EPMD_ADDRESS
环境变量完成的。
export ERL_EPMD_ADDRESS="::1"
默认情况下,RabbitMQ 节点在连接到 epmd
时将使用 IPv4 接口。配置为使用 IPv6 进行节点间通信的节点(参见上文)也将使用 IPv6 连接到 epmd
。
当 epmd
配置为独占使用 IPv6 但 RabbitMQ 节点未配置时,RabbitMQ 将记录类似于以下内容的错误消息
Protocol 'inet_tcp': register/listen error: econnrefused
systemd 单元文件
在使用 systemd 的发行版上,epmd.socket
服务控制 epmd
的网络设置。可以将 epmd
配置为仅侦听 IPv6 接口。
ListenStream=[::1]:4369
更新其单元文件后,需要重新加载服务。
systemctl daemon-reload
systemctl restart epmd.socket epmd.service
中间件:代理和负载均衡器
代理和负载均衡器通常用于在集群节点之间分配客户端连接。代理还有助于使客户端能够访问 RabbitMQ 节点而无需公开它们。中间件也会对连接产生副作用。
代理效果
代理和负载均衡器在客户端与其目标节点之间引入了额外的网络跃点(甚至多个跃点)。中间件也可能成为网络争用点:它们的吞吐量将成为整个系统的限制因素。因此,代理和负载均衡器的网络带宽超配和吞吐量监控非常重要。
中间件也可能在一段时间内没有活动时终止“空闲”TCP 连接。大多数情况下,这不可取。此类事件将导致服务器端出现连接关闭日志消息,并在客户端端出现 I/O 异常。
当在连接上启用心跳时,会导致周期性的轻量级网络流量。因此,心跳有一个副作用,即保护可以空闲一段时间 的客户端连接免受代理和负载均衡器过早关闭的影响。
10 到 30 秒的心跳超时将产生足够频繁的周期性网络流量(大约每 5 到 15 秒一次),以满足大多数代理工具和负载均衡器的默认值。太低的值将产生误报。
代理协议
RabbitMQ 支持代理协议版本 1(文本标头格式)和 2(二进制标头格式)。
该协议使 RabbitMQ 等服务器能够在连接通过代理(例如HAproxy或AWS ELB)时了解实际的客户端 IP 地址。这使得操作员更容易在管理 UI 或 CLI 工具中检查连接来源。
协议规范规定,出于安全原因,必须将其应用于所有连接或不应用于任何连接,此功能默认情况下处于关闭状态,需要为 RabbitMQ 支持的各个协议打开它。要为 AMQP 0-9-1 和 AMQP 1.0 客户端打开它
proxy_protocol = true
当代理协议开启时,除非客户端本身支持该协议,否则客户端将无法直接连接到 RabbitMQ。因此,当启用此选项时,所有客户端连接都必须通过也支持该协议并配置为发送代理协议标头的代理。HAproxy和AWS ELB文档说明了如何操作。
当代理协议开启并且连接通过兼容代理时,客户端库不需要任何操作或修改。通信对它们完全透明。
STOMP 和 MQTT,以及 Web STOMP 和 Web MQTT 都有各自的设置,可以启用对代理协议的支持。
TLS (SSL) 支持
可以使用 TLS 对连接进行加密。也可以使用对等证书进行身份验证。有关更多信息,请参阅 TLS/SSL 指南。
吞吐量调优
吞吐量调优是一个常见目标。可以通过以下方式进行改进:
- 增加 TCP 缓冲区大小
- 确保关闭 Nagle 算法
- 启用可选的 TCP 功能和扩展
对于后两者,请参见下面的操作系统级调优部分。
请注意,吞吐量调优会涉及权衡。例如,增加 TCP 缓冲区大小会增加每个连接使用的 RAM 量,这可能会导致服务器 RAM 使用量大幅增加。
TCP 缓冲区大小
这是关键的可调参数之一。每个 TCP 连接都会为其分配缓冲区。一般来说,缓冲区越大,每个连接使用的 RAM 越多,吞吐量越好。在 Linux 上,操作系统默认会自动调整 TCP 缓冲区大小,通常会稳定在 80 到 120 KB 之间。
为了获得最大的吞吐量,可以使用一组配置选项来增加缓冲区大小:
- AMQP 0-9-1 和 AMQP 1.0 使用
tcp_listen_options
- MQTT 使用
mqtt.tcp_listen_options
- STOMP 使用
stomp.tcp_listen_options
请注意,增加 TCP 缓冲区大小会增加每个客户端连接 节点使用的 RAM 量。
以下示例将 AMQP 0-9-1 连接的 TCP 缓冲区设置为 192 KiB:
tcp_listen_options.backlog = 128
tcp_listen_options.nodelay = true
tcp_listen_options.linger.on = true
tcp_listen_options.linger.timeout = 0
tcp_listen_options.sndbuf = 196608
tcp_listen_options.recbuf = 196608
MQTT 的相同示例:
mqtt.tcp_listen_options.backlog = 128
mqtt.tcp_listen_options.nodelay = true
mqtt.tcp_listen_options.linger.on = true
mqtt.tcp_listen_options.linger.timeout = 0
mqtt.tcp_listen_options.sndbuf = 196608
mqtt.tcp_listen_options.recbuf = 196608
以及 STOMP:
stomp.tcp_listen_options.backlog = 128
stomp.tcp_listen_options.nodelay = true
stomp.tcp_listen_options.linger.on = true
stomp.tcp_listen_options.linger.timeout = 0
stomp.tcp_listen_options.sndbuf = 196608
stomp.tcp_listen_options.recbuf = 196608
请注意,将发送和接收缓冲区大小设置为不同的值可能很危险,并且不建议这样做。
大量连接的调优
某些工作负载(通常称为“物联网”)假设每个节点有大量客户端连接,并且每个节点的流量相对较低。传感器网络就是这种工作负载:可能部署了数十万甚至数百万个传感器,每个传感器每隔几分钟就会发出数据。优化最大并发客户端数可能比优化总吞吐量更重要。
几个因素会限制单个节点可以支持多少个并发连接:
打开文件句柄限制
将最大打开文件句柄限制覆盖到更高的值时,必须相应地 覆盖 ERL_MAX_PORTS
环境变量。
大多数操作系统都限制了可以同时打开的文件句柄数。当 OS 进程(例如 RabbitMQ 的 Erlang VM)达到此限制时,它将无法打开任何新文件或接受任何新的 TCP 连接。此限制还会影响 Erlang 运行时 预先分配多少内存。这意味着在某些现代发行版上,此限制可能过高,需要降低。
如何覆盖限制
限制的配置方式 因操作系统和发行版而异,例如,取决于是否使用 systemd。对于 Linux,我们的 Debian 和 RPM 安装指南中提供了在 Linux 上控制系统限制的信息。网络上的许多资源都介绍了 Linux 内核限制管理,包括 打开文件句柄限制。
在使用 Docker 时,主机中的 Docker 守护进程配置文件 控制着限制。
MacOS 使用 类似的系统。
在 Windows 上,Erlang 运行时的限制仅使用 ERL_MAX_PORTS
环境变量控制。
ERL_MAX_PORTS 环境变量
运行时 有一个相关的限制,通过 ERL_MAX_PORTS
环境变量控制。
默认情况下,此限制通常设置为 65536。将最大打开文件句柄限制覆盖到更高的值时,必须相应地 覆盖 ERL_MAX_PORTS
。
要找出 RabbitMQ 节点的有效 ERL_MAX_PORTS
值,请使用以下命令:
rabbitmqctl eval 'erlang:system_info(port_limit).'
必要限制的基本估算
在针对并发连接数进行优化时,请确保您的系统有足够的文件描述符来支持不仅客户端连接,还有节点可能使用的文件。要计算一个大概的限制,请将每个节点的连接数乘以 1.5。例如,要支持 100,000 个连接,请将限制设置为 150,000。
稍微增加限制会增加空闲机器使用的 RAM 量,但这是一种合理的权衡。
每个连接的内存消耗:TCP 缓冲区大小
请参阅上一节以获取概述。
为了获得最大数量的并发客户端连接,可以使用一组配置选项来减小 TCP 缓冲区大小:
- AMQP 0-9-1 和 AMQP 1.0 使用
tcp_listen_options
- MQTT 使用
mqtt.tcp_listen_options
- STOMP 使用
stomp.tcp_listen_options
减小 TCP 缓冲区大小会减少每个客户端连接 节点使用的 RAM 量。
这在每个节点维持的并发连接数比吞吐量更重要的环境中通常是必要的。
以下示例将 AMQP 0-9-1 连接的 TCP 缓冲区设置为 32 KiB:
tcp_listen_options.backlog = 128
tcp_listen_options.nodelay = true
tcp_listen_options.linger.on = true
tcp_listen_options.linger.timeout = 0
tcp_listen_options.sndbuf = 32768
tcp_listen_options.recbuf = 32768
MQTT 的相同示例:
mqtt.tcp_listen_options.backlog = 128
mqtt.tcp_listen_options.nodelay = true
mqtt.tcp_listen_options.linger.on = true
mqtt.tcp_listen_options.linger.timeout = 0
mqtt.tcp_listen_options.sndbuf = 32768
mqtt.tcp_listen_options.recbuf = 32768
以及 STOMP:
stomp.tcp_listen_options.backlog = 128
stomp.tcp_listen_options.nodelay = true
stomp.tcp_listen_options.linger.on = true
stomp.tcp_listen_options.linger.timeout = 0
stomp.tcp_listen_options.sndbuf = 32768
stomp.tcp_listen_options.recbuf = 32768
请注意,降低 TCP 缓冲区大小会导致吞吐量成比例下降,因此需要为每个工作负载找到吞吐量和每个连接的 RAM 使用量之间的最佳值。
将发送和接收缓冲区大小设置为不同的值很危险,不建议这样做。不建议使用低于 8 KiB 的值。
减少统计信息发出时的 CPU 占用
大量并发连接会生成大量的指标(统计信息)发出事件。这会增加 CPU 消耗,即使在大多数连接处于空闲状态时也是如此。要减少这种占用,请使用 collect_statistics_interval
键增加统计信息收集间隔:
# sets the interval to 60 seconds
collect_statistics_interval = 60000
默认值为 5 秒(5000 毫秒)。
将间隔值增加到 30-60 秒可以减少 CPU 占用和峰值内存消耗。但这也有缺点:使用上述示例中的值,所述实体的指标将每 60 秒刷新一次。
这在 外部监控 的生产系统中是完全合理的,但会使管理 UI 对操作员来说不太方便使用。
限制连接上的通道数
通道也会消耗 RAM。通过优化应用程序使用的通道数,可以减少 RAM 消耗。可以使用 channel_max
配置设置限制连接上的最大通道数:
channel_max = 16
请注意,一些基于 RabbitMQ 客户端构建的库和工具可能隐式地需要一定数量的通道。200 以上的值很少需要。找到最佳值通常需要反复试验。
Nagle 算法(“nodelay”)
关闭 Nagle 算法 主要用于减少延迟,但也可以提高吞吐量。
kernel.inet_default_connect_options
和 kernel.inet_default_listen_options
必须包含 {nodelay, true}
以关闭节点间连接的 Nagle 算法。
配置服务于客户端连接的套接字时,tcp_listen_options
必须包含相同的选项。这是默认设置。
以下示例演示了这一点。首先,rabbitmq.conf
:
tcp_listen_options.backlog = 4096
tcp_listen_options.nodelay = true
这应该与 高级配置文件 中的以下部分一起使用:
[
{kernel, [
{inet_default_connect_options, [{nodelay, true}]},
{inet_default_listen_options, [{nodelay, true}]}
]}].
使用 经典配置格式 时,所有内容都在一个文件中配置:
[
{kernel, [
{inet_default_connect_options, [{nodelay, true}]},
{inet_default_listen_options, [{nodelay, true}]}
]},
{rabbit, [
{tcp_listen_options, [
{backlog, 4096},
{nodelay, true},
{linger, {true,0}},
{exit_on_close, false}
]}
]}
].
Erlang VM I/O 线程池调优
在针对大量并发连接进行调优时,合适的 Erlang VM I/O 线程池大小也很重要。请参阅上一节。
连接积压
在客户端数量较少的情况下,新连接速率分布非常不均匀,但也足够小,不会产生太大影响。当数量达到数万甚至更多时,务必确保服务器可以接受入站连接。未接受的 TCP 连接会被放入一个长度有限的队列中。此长度必须足以应对高峰负载时间和可能的峰值,例如,当许多客户端由于网络中断而断开连接或选择重新连接时。这可以使用 tcp_listen_options.backlog
选项进行配置:
tcp_listen_options.backlog = 4096
tcp_listen_options.nodelay = true
在 经典配置格式 中:
[
{rabbit, [
{tcp_listen_options, [
{backlog, 4096},
{nodelay, true},
{linger, {true, 0}},
{exit_on_close, false}
]}
]}
].
默认值为 128。当挂起的连接队列长度超过此值时,操作系统将拒绝连接。另请参见内核调优部分中的 net.core.somaxconn
。
处理高连接抖动
为什么高连接抖动是个问题?
具有高连接抖动(连接频繁打开和关闭)的工作负载需要调整 TCP 设置以避免耗尽某些资源:最大文件句柄数、RabbitMQ 节点上的 Erlang 进程、内核的临时端口范围(对于打开大量连接的主机,包括联合链接和Shovel连接)等等。耗尽这些资源的节点将无法接受新的连接,这将对整体系统可用性产生负面影响。
由于某些 TCP 功能和大多数现代 Linux 发行版的默认设置的组合,关闭的连接可以在一段时间后被检测到。这在心跳指南中有所介绍。这可能是导致连接累积的一个因素。另一个因素是TIME_WAIT
TCP 连接状态。该状态主要存在是为了确保来自关闭连接的重传段不会在具有相同客户端主机和端口的不同(较新)连接上“重新出现”。根据操作系统和 TCP 堆栈配置,连接可能会在此状态下停留数分钟,这在繁忙的系统中肯定会导致连接累积。
有关详细信息,请参阅在繁忙的服务器上处理 TCP TIME_WAIT 连接。
TCP 堆栈配置可以减少关闭状态下连接的峰值数量并避免资源耗尽,从而使节点能够始终接受新连接。
高连接抖动也可能意味着开发人员错误或对 RabbitMQ 支持的消息传递协议的使用方式存在错误假设。所有支持的协议都假设连接是长期存在的。不必要地打开并几乎立即关闭连接的应用程序会浪费资源(网络带宽、CPU、RAM)并加剧本节中描述的问题。
检查连接和收集证据
如果节点无法接受连接,则首先要收集数据(指标、证据)以确定系统的状态和限制因素(耗尽的资源)。可以使用诸如netstat、ss、lsof之类的工具来检查节点的 TCP 连接。有关示例,请参阅网络故障排除。
TCP Keepalives 的作用
虽然心跳足以检测失效连接,但在高连接抖动场景下它们是不够的。在这种情况下,应将心跳与TCP keepalives结合使用以加快断开连接的客户端检测速度。
减少在 TIME_WAIT 中花费的时间
TCP 堆栈调整还可以减少连接在TIME_WAIT
状态下花费的时间。特别是net.ipv4.tcp_fin_timeout
设置可以在这里提供帮助
net.ipv4.tcp_fin_timeout = 30
请注意,与以net.ipv4.
为前缀的其他设置一样,尽管名称如此,此设置也适用于 IPv4 和 IPv6 连接。
如果入站连接(来自客户端、插件、CLI 工具等)不依赖于 NAT,则可以将net.ipv4.tcp_tw_reuse
设置为1
(启用)以允许内核在TIME_WAIT
状态下重用套接字用于出站连接。此设置可以应用于客户端主机或中介(例如代理和负载均衡器)。请注意,如果使用 NAT,则此设置不安全,并且可能导致难以跟踪的问题。
上述设置通常应与减少的TCP keepalive值结合使用,例如
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time=30
net.ipv4.tcp_keepalive_intvl=10
net.ipv4.tcp_keepalive_probes=4
net.ipv4.tcp_tw_reuse = 1
操作系统级调整
操作系统设置会影响 RabbitMQ 的运行。有些与网络直接相关(例如 TCP 设置),有些则会影响 TCP 套接字以及其他内容(例如打开的文件句柄限制)。
了解这些限制非常重要,因为它们可能会根据工作负载而改变。
关键的相关内核选项
一些重要的可配置内核选项包括(请注意,尽管选项名称如此,但它们对 IPv4 和 IPv6 连接都有效)
内核设置 | 描述 |
---|---|
fs.file-max | 内核将分配的最大文件数。可以使用 |
net.ipv4.ip_local_port_range | 本地 IP 端口范围,定义为一对值。该范围必须为并发连接的峰值数量提供足够的条目。 |
net.ipv4.tcp_tw_reuse | 启用后,允许内核在安全的情况下重用 |
net.ipv4.tcp_fin_timeout | 将此超时时间降低到 15-30 秒范围内的值可以减少关闭连接在 TIME_WAIT 状态下停留的时间。请参阅处理高连接抖动。 |
net.core.somaxconn | 侦听队列的大小(同时正在建立连接的数量)。默认为 128。增加到 4096 或更高以支持入站连接突发,例如当客户端大量重新连接时。 |
net.ipv4.tcp_max_syn_backlog | 尚未收到连接客户端确认的已记住连接请求的最大数量。默认为 128,最大值为 65535。当优化吞吐量时,建议的起始值为 4096 和 8192。 |
net.ipv4.tcp_keepalive_* |
AMQP 0-9-1 和 STOMP 具有心跳,这部分抵消了其效果,即检测无响应的对等方(例如,在硬件或电源故障的情况下)可能需要数分钟时间。MQTT 也有自己的 keepalives 机制,它在不同的名称下具有相同的想法。 启用 TCP keepalive 并使用默认设置时,我们建议将心跳超时时间设置为 8-20 秒。另请参阅本指南后面有关 TCP keepalives 的说明。 |
net.ipv4.conf.default.rp_filter | 激活或打开反向路径过滤。如果IP 地址欺骗不是您系统关心的问题,请将其停用。 |
请注意,这些选项的默认值在 Linux 内核版本和发行版之间有所不同。建议使用较新的内核版本(例如 6.x 或更高版本)。
基于 sysctl 的配置
内核参数调整因操作系统而异。本指南重点介绍 Linux。要以交互方式配置内核参数,请使用sysctl -w
(需要超级用户权限),例如
sysctl -w fs.file-max 200000
要使更改永久生效(在重新引导之间保持不变),需要将其添加到/etc/sysctl.conf
中。有关更多详细信息,请参阅sysctl(8)和sysctl.conf(5)。
TCP 堆栈调整是一个广泛的主题,在其他地方有详细介绍
TCP 套接字选项
常用选项
内核设置 | 描述 |
---|---|
tcp_listen_options.nodelay | 设置为 |
tcp_listen_options.sndbuf | 请参阅本指南前面有关 TCP 缓冲区的讨论。默认值由操作系统自动调整,在现代 Linux 版本上通常在 88 KiB 到 128 KiB 范围内。增加缓冲区大小可以提高每个连接的消费者吞吐量和 RAM 使用率。减少具有相反的效果。 |
tcp_listen_options.recbuf | 请参阅本指南前面有关 TCP 缓冲区的讨论。默认值的效果类似于 |
tcp_listen_options.backlog | 未接受的 TCP 连接队列的最大大小。当达到此大小限制时,将拒绝新的连接。对于具有数千个并发连接和可能的批量客户端重新连接的环境,将其设置为 4096 或更高。 |
tcp_listen_options.keepalive | 设置为 |
默认值
以下是 RabbitMQ 使用的默认 TCP 套接字选项配置
- TCP 连接积压限制为 128 个连接
- Nagle 算法已停用
- 服务器套接字保持活动状态已启用,超时时间为 0
心跳
RabbitMQ 支持的一些协议,包括 AMQP 0-9-1,支持心跳,这是一种更快地检测死 TCP 对等方的方法。有关更多信息,请参阅心跳指南。
网络滴答时间
心跳用于检测客户端和 RabbitMQ 节点之间对等方或连接故障。net_ticktime用于集群节点通信,具有相同目的。低于 5(秒)的值可能会导致误报,因此不建议使用。
TCP Keepalives
TCP 包含一个与消息传递协议中的心跳(又名 keepalive)和上面介绍的网络滴答超时具有相同目的的机制:TCP keepalives。由于默认设置不足,TCP keepalives 通常无法按预期工作:检测死对等方需要很长时间(例如,一小时或更长时间)。但是,通过调整,它们可以起到与心跳相同的作用,并清理陈旧的 TCP 连接,例如,对于选择不使用心跳的客户端(有意或无意)。
以下是在 70 秒后将 TCP 连接视为已死或不可达的 TCP keepalives 的 sysctl 配置示例(连接空闲 30 秒后,每 10 秒尝试 4 次)
net.ipv4.tcp_keepalive_time=30
net.ipv4.tcp_keepalive_intvl=10
net.ipv4.tcp_keepalive_probes=4
在RabbitMQ Operator无法控制应用程序设置或使用的客户端库的环境中,TCP keepalives可以作为一种有用的额外防御机制。
连接握手超时
RabbitMQ对连接握手有一个超时时间,默认为10秒。当客户端在资源受限的环境中运行时,可能需要增加超时时间。这可以通过rabbit.handshake_timeout
(以毫秒为单位)来完成。
handshake_timeout = 20000
需要指出的是,这只有在客户端和网络非常受限的情况下才需要。其他情况下出现的握手超时表明其他地方存在问题。
TLS握手
如果启用了TLS,可能还需要增加TLS握手超时时间。这可以通过rabbit.ssl_handshake_timeout
(以毫秒为单位)来完成。
ssl_handshake_timeout = 10000
主机名解析和DNS
在许多情况下,RabbitMQ依赖于Erlang运行时进行节点间通信(包括rabbitmqctl
、rabbitmq-plugins
等工具)。客户端库在连接到RabbitMQ节点时也会执行主机名解析。本节简要介绍了与此相关的大多数常见问题。
由客户端库执行
如果客户端库被配置为连接到主机名,它将执行主机名解析。根据DNS和本地解析器(/etc/hosts
等)配置,这可能需要一些时间。错误的配置可能导致解析超时,例如,当尝试通过DNS解析本地主机名(如my-dev-machine
)时。结果,客户端连接可能需要很长时间(从几十秒到几分钟)。
短主机名和完整限定的RabbitMQ节点名称
RabbitMQ依赖于Erlang运行时进行节点间通信。Erlang节点包括主机名,可以是短主机名(rmq1
)或完整限定的主机名(rmq1.dev.megacorp.local
)。运行时不允许混合使用短主机名和完整限定的主机名。集群中的每个节点都必须能够解析其他每个节点的主机名,无论是短主机名还是完整限定的主机名。
默认情况下,RabbitMQ将使用短主机名。设置RABBITMQ_USE_LONGNAME
环境变量以使RabbitMQ节点使用完整限定的名称,例如rmq1.dev.megacorp.local
。
反向DNS查找
如果reverse_dns_lookups
配置选项设置为true
,RabbitMQ将完成客户端IP地址的反向DNS查找,并在连接信息中列出主机名(例如,在管理UI中)。
如果节点的主机名解析配置不佳,反向DNS查找可能会花费很长时间。这可能会增加接受客户端连接时的延迟。
要显式激活或开启反向DNS查找
reverse_dns_lookups = true
要停用反向DNS查找
reverse_dns_lookups = false
inetrc文件
该Erlang运行时允许使用称为inetrc文件的文件来调整许多与主机名解析相关的设置。
可以通过使用RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS
环境变量添加额外的运行时参数来指定文件路径。
- bash
- PowerShell
export RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="-kernel inetrc '/path/to/inetrc.file'"
$env:ERL_INETRC = "-kernel inetrc 'c:\path\to\inetrc.file'"
该文件可用于配置节点上与主机名解析相关的许多设置(而非系统范围的设置)。
- 主机名和主机地址(类似于本地主机文件)。
- 本地域名。
- 域名服务器。
- 首选的主机名查找方法(例如,本地主机文件与DNS)。
- 主机名缓存间隔。
- 搜索域。
请查阅inetrc文件文档以了解更多信息。
验证主机名解析
由于主机名解析是节点间通信成功的前提条件,因此CLI工具提供了两个命令来帮助验证节点上的主机名解析是否按预期工作。这些命令并非旨在取代dig
和其他专门的DNS工具,而是提供了一种在考虑Erlang运行时主机名解析器功能的情况下执行最基本检查的方法。
第一个命令是rabbitmq-diagnostics resolve_hostname
# resolves node2.cluster.local.svc to IPv6 addresses on node rabbit@node1.cluster.local.svc
rabbitmq-diagnostics resolve_hostname node2.cluster.local.svc --address-family IPv6 -n rabbit@node1.cluster.local.svc
# makes local CLI tool resolve node2.cluster.local.svc to IPv4 addresses
rabbitmq-diagnostics resolve_hostname node2.cluster.local.svc --address-family IPv4 --offline
第二个命令是rabbitmq-diagnostics resolver_info
rabbitmq-diagnostics resolver_info
它将报告关键的解析器设置,例如查找顺序(CLI工具是否应优先使用OS解析器、inetrc文件等),以及任何inetrc主机名条目。
Runtime Hostname Resolver (inetrc) Settings
Lookup order: native
Hosts file: /etc/hosts
Resolver conf file: /etc/resolv.conf
Cache size:
inetrc File Host Entries
(none)
连接事件日志记录
请参阅日志记录指南中的连接生命周期事件。
网络连接故障排除
在单独的指南中介绍了网络相关问题的故障排除方法。
MacOS应用程序防火墙
在启用了应用程序防火墙的MacOS系统上,必须允许Erlang运行时进程绑定到端口并接受连接。否则,RabbitMQ节点将无法绑定到其端口,并且将无法启动。
可以在系统设置中的“安全与隐私”=>“防火墙”下查看已阻止应用程序的列表。
要“解除阻止”命令行工具,请使用sudo /usr/libexec/ApplicationFirewall/socketfilterfw
。以下示例假设Erlang安装在/usr/local/Cellar/erlang/{version}
下,由Homebrew Erlang公式使用。
# allow CLI tools and shell to bind to ports and accept inbound connections
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add /usr/local/Cellar/erlang/{version}/lib/erlang/bin/erl
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblockapp /usr/local/Cellar/erlang/{version}/lib/erlang/bin/erl
# allow server nodes (Erlang VM) to bind to ports and accept inbound connections
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add /usr/local/Cellar/erlang/{version}/lib/erlang/erts-{erts version}/bin/beam.smp
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblockapp /usr/local/Cellar/erlang/{version}/lib/erlang/erts-{erts version}/bin/beam.smp
请注意,socketfilterfw
命令行参数在MacOS版本之间可能有所不同。要查看支持的命令行参数,请使用
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --help