网络和 RabbitMQ
概述
客户端通过网络与 RabbitMQ 通信。broker 支持的所有协议都是基于 TCP 的。RabbitMQ 和操作系统都提供了许多可以调整的旋钮。其中一些与 TCP 和 IP 操作直接相关,另一些与应用程序级协议(如 TLS)有关。本指南涵盖了 RabbitMQ 上下文中与网络相关的多个主题。
讨论的一些可调参数是特定于操作系统的。本指南在涵盖特定于操作系统的科目时侧重于 Linux,因为它是 RabbitMQ 部署最常见的平台。
网络是一个非常广泛的主题。因此,本指南涵盖了多个主题,例如
- 接口 节点监听客户端连接
- IP 版本首选项:双栈、仅 IPv6 和 仅 IPv4
- 端口 客户端使用的端口、集群中 节点间流量 和 CLI 工具
- IPv6 支持 节点间流量
- TLS 用于客户端连接
- 针对 大量并发连接 进行调优
- 高客户端连接流失 场景和资源耗尽
- TCP 缓冲区大小(影响 吞吐量 和 每个连接使用的内存量)
- 主机名解析 相关主题,例如 反向 DNS 查找
- 节点间通信 接口和端口
- epmd 及其在节点间通信中扮演的角色
- 如何 暂停和恢复监听器 以临时停止和恢复新的客户端连接
- 其他 TCP 套接字设置
- 代理协议 支持客户端连接
- 内核 TCP 设置和限制(例如 TCP keepalive 和 打开文件句柄限制)
- 当 MacOS 应用程序防火墙 启用时,如何允许 Erlang 运行时接受入站连接
- 操作系统级别调优 与网络相关
除了操作系统内核参数和 DNS 之外,所有 RabbitMQ 设置都通过 RabbitMQ 配置文件 进行配置。
网络是一个广泛的主题。许多配置选项可能对某些工作负载产生正面或负面影响。因此,本指南不试图成为一个完整的参考,而是提供关键可调参数的索引,并作为起点。
此外,本指南还涉及一些与网络密切相关的主题,例如
- 主机名、主机名解析和 DNS
- 连接生命周期日志记录
- 心跳(又名 keepalive)
- 代理和负载均衡器
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 以仅监听 IPv4 和 IPv6 的 localhost
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:用于 stream 复制
- 25672:用于节点间和 CLI 工具通信(Erlang 分布式服务器端口),并从动态范围分配(默认情况下限制为单个端口,计算为 AMQP 端口 + 20000)。除非确实需要在这些端口上进行外部连接(例如,集群使用 federation 或 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 工具的入站连接。重要的是仅将这些端口暴露给运行其他集群节点的主机和子网,或使用 CLI 工具的位置,并且 不暴露给公共互联网。
节点间通信接口
为了配置 RabbitMQ 节点间通信监听器仅监听特定地址,请在 rabbitmq.conf
中使用 distribution.listener.interface
。
# Instructs the node to only listen for inter-node communication connections on a local interface.
# This affects both connections from cluster peers and CLI tools.
distribution.listener.interface = 192.168.10.84
# Limit inter-node communication listener to a local interface (using an IPv4 address).
#
# This particular configuration only makes sense for single-node clusters.
# For multi-node clusters, nodes must listen on an "internal network-local" interface
# that would allow cluster peers to connect but not be exposed to the public Internet
distribution.listener.interface = 127.0.0.1
# Limit inter-node communication listener to a local interface (using an IPv6 address).
#
# This particular configuration only makes sense for single-node clusters.
# For multi-node clusters, nodes must listen on an "internal network-local" interface
# that would allow cluster peers to connect but not be exposed to the public Internet
distribution.listener.interface = ::1
节点间通信端口范围
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 工具的每个主机的远程连接访问节点间通信端口。epmd 端口必须为 CLI 工具和集群功能打开。
在 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
环境变量完成
# instructs epmd to only listen on a local interface
export ERL_EPMD_ADDRESS="::1"
默认情况下,RabbitMQ 节点在连接到 epmd
时将使用 IPv4 接口。配置为 使用 IPv6 进行节点间通信的节点也将使用 IPv6 连接到 epmd
。
当 epmd
配置为独占使用 IPv6 但 RabbitMQ 节点未配置时,RabbitMQ 将记录类似于此的错误消息
Protocol 'inet_tcp': register/listen error: econnrefused
为了配置 RabbitMQ 节点间通信监听器仅监听特定地址,请在 rabbitmq.conf
中使用 distribution.listener.interface
。
# Instructs the node to only listen for inter-node communication connections on a local interface.
# This affects both connections from cluster peers and CLI tools.
#
# This particular configuration only makes sense for single-node clusters.
# For multi-node clusters, nodes must listen on an "internal network-local" interface
# that would allow cluster peers to connect but not be exposed to the public Internet
distribution.listener.interface = ::1
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 加密与 RabbitMQ 的连接。也可以使用对等证书进行身份验证。请参阅 TLS/SSL 指南 了解更多信息。
吞吐量调优
吞吐量调优是一个常见的目标。可以通过以下方式实现改进
从 RabbitMQ 4.1 开始,连接会根据消息速率和大小自动调整其 TCP 缓冲区大小。
- 确保 Nagle 算法已关闭
- 启用可选的 TCP 功能和扩展
- 增加(初始)连接 TCP 缓冲区大小
对于后两者,请参阅下面的操作系统级别调优部分。
请注意,吞吐量调优将涉及权衡。例如,增加 TCP 缓冲区大小将增加每个连接使用的 RAM 量,这可能会显着增加服务器 RAM 总使用量。
TCP 缓冲区大小
从 RabbitMQ 4.1 开始,连接会根据消息速率和大小自动调整其 TCP 缓冲区大小。
因此,手动调整的 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
# Starting with RabbitMQ 4.1, connections automatically adjust their TCP buffer size
# based on message rates and sizes.
#
# Manually adjusted TCP buffer size is therefore
# only has an effect very early on in the connection's lifetime,
# and has become significantly less relevant than in earlier series.
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
# Starting with RabbitMQ 4.1, connections automatically adjust their TCP buffer size
# based on message rates and sizes.
#
# Manually adjusted TCP buffer size is therefore
# only has an effect very early on in the connection's lifetime,
# and has become significantly less relevant than in earlier series.
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
# Starting with RabbitMQ 4.1, connections automatically adjust their TCP buffer size
# based on message rates and sizes.
#
# Manually adjusted TCP buffer size is therefore
# only has an effect very early on in the connection's lifetime,
# and has become significantly less relevant than in earlier series.
stomp.tcp_listen_options.sndbuf = 196608
stomp.tcp_listen_options.recbuf = 196608
请注意,将发送和接收缓冲区大小设置为不同的值可能是危险的,并且不建议这样做。
针对大量连接进行调优
从 RabbitMQ 4.1 开始,连接会根据消息速率和大小自动调整其 TCP 缓冲区大小。
因此,手动调整的 TCP 缓冲区大小仅在连接生命周期的早期有效,并且与早期系列相比,相关性已大大降低。
一些工作负载(通常称为“物联网”)假定每个节点有大量客户端连接,并且每个节点的流量相对较低。一种这样的工作负载是传感器网络:可以部署数十万或数百万个传感器,每个传感器每隔几分钟发出数据。针对最大并发客户端数进行优化可能比针对总吞吐量更重要。
多个因素可能会限制单个节点可以支持的并发连接数
打开文件句柄限制
当将最大打开文件句柄限制覆盖为更高的值时,必须相应地 覆盖 ERL_MAX_PORTS
环境变量。
大多数操作系统限制可以同时打开的文件句柄数。当操作系统进程(例如 RabbitMQ 的 Erlang VM)达到限制时,它将无法打开任何新文件或接受更多 TCP 连接。该限制还会影响 Erlang 运行时 预先分配多少内存。这意味着某些现代发行版上的限制 可能过高 并且需要降低。
如何覆盖限制
限制的配置方式 因操作系统和发行版而异,例如,取决于是否使用 systemd。对于 Linux,我们的 Debian 和 RPM 安装指南中提供了 Linux 上控制系统限制。Web 上有许多资源涵盖了 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 进程数、内核的临时端口范围(对于打开大量连接的主机,包括 Federation 链接和 Shovel 连接)等。资源耗尽的节点将无法接受新连接,这将对整体系统可用性产生负面影响。
由于某些 TCP 功能和大多数现代 Linux 发行版的默认设置的组合,关闭的连接可以在很长一段时间后才被检测到。这在 心跳指南 中进行了介绍。这可能是连接累积的一个促成因素。另一个是 TIME_WAIT
TCP 连接状态。此状态主要存在是为了确保来自已关闭连接的重传段不会在具有相同客户端主机和端口的不同(较新的)连接上“重新出现”。根据操作系统和 TCP 堆栈配置,连接可能会在此状态下花费数分钟,这在繁忙的系统上肯定会导致连接累积。
有关详细信息,请参阅 应对繁忙服务器上的 TCP TIME_WAIT 连接。
TCP 堆栈配置可以减少关闭状态的峰值连接数,并避免资源耗尽,从而使节点始终可以接受新连接。
高连接流失也可能意味着开发人员错误或对 RabbitMQ 支持的消息传递协议的预期使用方式的错误假设。所有支持的协议都假定连接是长期的。不必要地打开并几乎立即关闭连接的应用程序会浪费资源(网络带宽、CPU、RAM),并导致本节中描述的问题。
检查连接并收集证据
如果节点无法接受连接,首先收集数据(指标、证据)以确定系统状态和限制因素(资源耗尽)非常重要。可以使用 netstat、ss、lsof 等工具来检查节点的 TCP 连接。有关示例,请参阅 网络故障排除。
TCP Keepalive 的作用
虽然 心跳 足以检测已失效的连接,但在高连接流失的情况下,它们是不够的。在这些情况下,心跳应与 TCP Keepalive 结合使用,以加速断开连接的客户端检测。
减少在 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 也有自己的 Keepalive 机制,其概念相同,名称不同。 当使用默认设置启用 TCP Keepalive 时,我们建议将心跳超时设置为 8-20 秒。另请参阅本指南后面关于 TCP Keepalive 的说明。 |
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 对等方的方法。有关更多信息,请参阅 心跳指南。
网络 Tick 时间
心跳 用于检测客户端和 RabbitMQ 节点之间的对等方或连接故障。net_ticktime 的用途相同,但用于集群节点通信。低于 5(秒)的值可能会导致误报,不建议使用。
TCP Keepalive
TCP 包含一种与上述消息传递协议和网络 Tick 超时中的心跳(又名 Keepalive)机制目的相似的机制:TCP Keepalive。由于默认值不充分,TCP Keepalive 通常无法按预期方式工作:检测到死对等方需要很长时间(例如,一小时或更长时间)。但是,通过调优,它们可以像心跳一样达到相同的目的,并清理陈旧的 TCP 连接,例如,对于选择不使用心跳的客户端(有意或无意)。
以下是 TCP Keepalive 的 sysctl 配置示例,该示例认为 TCP 连接在 70 秒后死亡或无法访问(在连接空闲 30 秒后,每 10 秒尝试 4 次)
net.ipv4.tcp_keepalive_time=30
net.ipv4.tcp_keepalive_intvl=10
net.ipv4.tcp_keepalive_probes=4
在 RabbitMQ 运营商无法控制应用程序设置或使用的客户端库的环境中,TCP Keepalive 可以作为一种有用的附加防御机制。
连接握手超时
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 工具是应首选操作系统解析器、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