跳至主内容
版本:4.3

网络与 RabbitMQ

概述

客户端通过网络与 RabbitMQ 进行通信。代理支持的所有协议均基于 TCP。RabbitMQ 和操作系统都提供了一些可以调整的参数。其中一些与 TCP 和 IP 操作直接相关,另一些则与 TLS 等应用层协议有关。本指南涵盖了 RabbitMQ 网络环境中的多个主题。

所讨论的一些可调参数是特定于操作系统的。由于 Linux 是 RabbitMQ 部署最常见的平台,本指南在涉及操作系统特定主题时将重点关注 Linux。

网络是一个非常广泛的话题。因此,本指南涵盖了多个主题,例如:

除操作系统内核参数和 DNS 外,所有 RabbitMQ 设置均通过 RabbitMQ 配置文件进行配置

网络是一个广泛的话题。许多配置选项对某些工作负载可能会产生积极或消极的影响。因此,本指南并不试图成为一份完整的参考手册,而是提供关键可调参数的索引,并作为一个起点。

此外,本指南还涉及一些与网络密切相关的主题,例如:

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:

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 客户端、管理 UIrabbitmqadmin,分别不带和带 TLS(仅当启用 管理插件 时)
  • 61613、61614:STOMP 客户端(无 TLS 和带 TLS)(仅当 STOMP 插件 启用时)
  • 1883、8883:MQTT 客户端(无 TLS 和带 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 Port Mapping Daemon,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_MINRABBITMQ_CTL_DIST_PORT_MAX 环境变量进行配置。请注意,将范围限制为单个端口将阻止同一主机上同时运行多个 CLI 工具,并可能影响需要与多个集群节点并行连接的 CLI 命令。因此,建议端口范围为 10。

在配置防火墙规则时,必须允许来自每个集群节点 IP 地址以及可能使用 CLI 工具的每个主机的对节点间通信端口的远程连接。epmd 端口必须打开,CLI 工具和集群才能正常工作。

在 Windows 上,当 RabbitMQ 作为服务运行时,以下设置无效。详情请参阅Windows 配置

RabbitMQ 使用的范围也可以通过 rabbitmq.conf 中的两个配置键进行控制:

  • distribution.listener.port_range.min
  • distribution.listener.port_range.max

它们定义了范围的下限和上限(包含)。

下面的示例使用了单端口范围,但该值不同于默认值:

distribution.listener.port_range.min = 33672
distribution.listener.port_range.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 的值。

在节点间通信(及 CLI 工具)中使用 IPv6

除了专有 IPv6 客户端连接之外,节点还可以配置为仅使用 IPv6 进行节点间和 CLI 工具连接。

这涉及多个地方的配置:

  • 运行时中的节点间通信协议设置
  • 配置 CLI 工具使用的 IPv6
  • epmd,一个参与节点间通信(发现)的服务

可以实现节点间和 CLI 工具通信使用 IPv6,而客户端连接使用 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_inetrcerl_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 支持 Proxy 协议版本 1(文本头格式)和版本 2(二进制头格式)。

该协议使 RabbitMQ 等服务器能够在连接经过代理(例如 HAproxyAWS ELB)时感知到实际的客户端 IP 地址。这使得操作员更容易在管理 UI 或 CLI 工具中检查连接源。

协议规范规定出于安全原因,它必须应用于所有连接或根本不应用,此功能默认关闭,需要针对 RabbitMQ 支持的各个协议手动开启。要为 AMQP 0-9-1 和 AMQP 1.0 客户端开启:

proxy_protocol = true

当开启 Proxy 协议后,客户端除非本身也支持该协议,否则无法直接连接到 RabbitMQ。因此,当启用此选项时,所有客户端连接都必须通过一个也支持该协议并配置为发送 Proxy 协议头的代理。HAproxyAWS ELB 的文档解释了如何实现这一点。

当开启 Proxy 协议且连接通过兼容的代理时,客户端库无需采取任何行动或进行任何修改。通信对它们而言是完全透明的。

STOMPMQTT,以及 Web STOMPWeb MQTT 都有其各自的设置来启用对 Proxy 协议的支持。

TLS (SSL) 支持

可以通过 TLS 对 RabbitMQ 的连接进行加密。使用对等证书进行身份验证也是可能的。请参考 TLS/SSL 指南以获取更多信息。

针对吞吐量和多吉比特链路的调优

调优吞吐量是一个常见目标。改进可以通过以下方式实现:

  • 确保禁用 Nagle 算法。
  • 开启可选的 TCP 功能和扩展。
  • 增加(初始)连接 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 缓冲区大小仅在连接生命周期的早期阶段产生影响,并且与早期版本相比,其相关性已显著降低。

某些工作负载(通常称为“物联网”)假设每个节点有大量的客户端连接,且每个节点的流量相对较低。传感器网络就是其中之一:可能有数十万或数百万个传感器部署,每个传感器每隔几分钟发送一次数据。针对最大并发客户端数量进行优化可能比针对总吞吐量进行优化更为重要。

有几个因素会限制单个节点可以支持的并发连接数:

  • 最大打开文件句柄数(包括套接字)以及其他内核强制执行的资源限制。
  • 每个连接使用的 RAM 量
  • 每个连接使用的 CPU 资源量。
  • 虚拟机配置允许的最大 Erlang 进程数。

打开文件句柄限制

重要

当将最大打开文件句柄限制覆盖为更高值时,必须相应地覆盖 ERL_MAX_PORTS 环境变量。

大多数操作系统限制了可以同时打开的文件句柄数量。当操作系统进程(如 RabbitMQ 的 Erlang 虚拟机)达到该限制时,它将无法打开任何新文件或接受更多 TCP 连接。该限制还会影响 Erlang 运行时预先分配的内存量。这意味着某些现代发行版上的限制可能过高,需要调低。

如何覆盖限制

限制的配置方式因操作系统和发行版而异,例如是否使用 systemd。对于 Linux,我们的DebianRPM 安装指南提供了关于控制 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。通过优化应用程序使用的通道数量,可以减少该数量。可以使用 channel_max 配置设置来限制连接上的最大通道数:

channel_max = 16

请注意,一些构建在 RabbitMQ 客户端之上的库和工具可能隐含地需要一定数量的通道。超过 200 的值很少是必需的。找到一个最优值通常是试错的过程。

Nagle 算法 ("nodelay")

关闭 Nagle 算法主要有助于减少延迟,但也可能提高吞吐量。

kernel.inet_default_connect_optionskernel.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 虚拟机 I/O 线程池调优

在调优大量并发连接时,足够的 Erlang 虚拟机 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 栈配置,连接可能在此状态下停留数分钟,在繁忙的系统上,这必然会导致连接堆积。

详见Coping with the TCP TIME_WAIT connections on busy servers

TCP 栈配置可以减少处于关闭状态的峰值连接数,并避免资源耗尽,从而使节点始终能够接受新连接。

高连接流失也可能意味着开发人员的错误或对 RabbitMQ 支持的消息协议使用方式的错误假设。所有支持的协议都假设连接是长寿命的。不必要地打开并几乎立即关闭连接的应用程序浪费资源(网络带宽、CPU、RAM),并加剧了本节描述的问题。

检查连接和收集证据

如果节点无法接受连接,请务必首先收集数据(指标、证据)以确定系统状态和限制因素(耗尽的资源)。可以使用诸如 netstatsslsof 等工具来检查节点的 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

内核将分配的最大文件数。限制和当前值可以使用 /proc/sys/fs/file-nr 查看。

net.ipv4.ip_local_port_range

本地 IP 端口范围,定义为一对值。该范围必须为并发连接的峰值数量提供足够的条目。

net.ipv4.tcp_tw_reuse

启用时,允许内核在安全的情况下重用 TIME_WAIT 状态下的套接字。参见处理高连接流失。当客户端和对等点通过 NAT 连接时,此选项是危险的。

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_*

net.ipv4.tcp_keepalive_timenet.ipv4.tcp_keepalive_intvlnet.ipv4.tcp_keepalive_probes 配置 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

设置为 true 时,禁用 Nagle 算法。默认为 true。强烈推荐大多数用户使用。

tcp_listen_options.sndbuf

参见本指南前文对 TCP 缓冲区的讨论。默认值由操作系统自动调优,在现代 Linux 版本上通常在 88 KiB 到 128 KiB 范围内。增加缓冲区大小可提高消费者吞吐量和每个连接的 RAM 使用量。减小则产生相反效果。

tcp_listen_options.recbuf

参见本指南前文对 TCP 缓冲区的讨论。其默认值的影响与 tcp_listen_options.sndbuf 类似,但针对发布者和通用协议操作。

tcp_listen_options.backlog

未接受的 TCP 连接队列的最大大小。达到此大小后,新连接将被拒绝。对于具有数千个并发连接和可能的批量客户端重新连接的环境,请设置为 4096 或更高。

tcp_listen_options.keepalive

设置为 true 时,启用 TCP keepalives(见上文)。默认为 false。适用于连接可能长时间(至少 10 分钟)空闲的环境,尽管仍建议在此选项之上使用心跳

默认值

以下是 RabbitMQ 使用的默认 TCP 套接字选项配置:

  • TCP 连接积压限制为 128 个连接
  • 禁用 Nagle 算法
  • 服务器套接字保留(lingering)已启用,超时为 0

心跳

RabbitMQ 支持的一些协议(包括 AMQP 0-9-1)支持心跳,这是一种更快检测死掉的 TCP 对等点的方法。请参阅 心跳指南以获取更多信息。

Net Tick Time

心跳用于检测客户端与 RabbitMQ 节点之间的对等点或连接故障。net_ticktime 用于集群节点通信。小于 5(秒)的值可能导致误报,不建议使用。

TCP Keepalives

TCP 包含一种机制,其目的类似于消息协议中的心跳(即 keepalive)和上述网络滴答超时:TCP keepalives。由于默认设置不当,TCP keepalives 通常无法按预期工作:检测死掉的对等点需要很长时间(例如一小时或更久)。然而,经过调优,它们可以起到与心跳相同的目的,并清理陈旧的 TCP 连接,例如与选择不使用心跳的客户端连接(无论是否是有意的)。

以下是 TCP keepalives 的 sysctl 配置示例,它在 70 秒后将 TCP 连接视为死亡或不可达(连接空闲 30 秒后,每 10 秒尝试 4 次):

net.ipv4.tcp_keepalive_time=30
net.ipv4.tcp_keepalive_intvl=10
net.ipv4.tcp_keepalive_probes=4

在 RabbitMQ 操作员无法控制所使用的应用程序设置或客户端库的环境中,TCP keepalives 可以成为一种有用的额外防御机制。

连接握手超时

RabbitMQ 有一个连接握手超时,默认为 10 秒。当客户端在极其受限的环境中运行时,可能需要增加该超时时间。这可以通过 rabbit.handshake_timeout(以毫秒为单位)完成:

handshake_timeout = 20000

应该指出的是,这仅在极其受限的客户端和网络中才是必要的。在其他情况下,握手超时通常表明别处存在问题。

TLS 握手

如果启用了 TLS,可能还需要增加 TLS 握手超时。这可以通过 rabbit.ssl_handshake_timeout(以毫秒为单位)完成:

ssl_handshake_timeout = 10000

主机名解析和 DNS

在许多情况下,RabbitMQ 依赖 Erlang 运行时进行节点间通信(包括 rabbitmqctlrabbitmq-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 查询可能需要很长时间。这会增加接受客户端连接时的延迟。

要显式激活或开启反向 DNS 查询:

reverse_dns_lookups = true

要停用反向 DNS 查询:

reverse_dns_lookups = false

inetrc 文件

Erlang 运行时允许使用一种称为 inetrc 文件的文件来调优许多与主机名解析相关的设置。

可以通过使用 RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS 环境变量添加额外的运行时参数来指定该文件的路径:

export RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="-kernel inetrc '/path/to/inetrc.file'"

该文件可用于配置节点上(非系统范围)许多与主机名解析相关的设置:

  • 主机名和主机地址(类似于本地主机文件
  • 本地域名
  • 域名服务器
  • 首选主机名查找方法(例如本地主机文件 vs 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
© . This site is unofficial and not affiliated with VMware.