跳至主内容
版本:4.2

网络和 RabbitMQ

概述

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

讨论的一些可调参数是操作系统特定的。本指南在介绍操作系统特定主题时,侧重于 Linux,因为它是 RabbitMQ 最常部署的平台。

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

除了操作系统内核参数和 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 以仅在 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 和带 TLS
  • 5552, 5551:用于 RabbitMQ Stream 协议客户端,不带 TLS 和带 TLS
  • 6000 到 6500:用于 Stream 复制
  • 25672:用于节点间和 CLI 工具通信(Erlang 分布服务器端口),并从动态范围分配(默认限制为单个端口,计算方式为 AMQP 端口 + 20000)。除非这些端口上的外部连接确实必要(例如,集群使用 Federation 或在集群外部的机器上使用 CLI 工具),否则不应公开这些端口。
  • 35672-35682:此客户端 TCP 端口范围由 CLI 工具用于与节点通信。默认情况下,范围计算为 (服务器分发端口 + 10000)(服务器分发端口 + 10010)
  • 15672, 15671:HTTP API 客户端、管理 UIrabbitmqadmin,不带 TLS 和带 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 和带 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_MINRABBITMQ_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 环境变量(以 KB 为单位)进行控制。默认值为 128 MB(128000 kB)。

在节点间流量繁重的集群中,增加此值可能会对吞吐量产生积极影响。不建议使用低于 64 MB 的值。

使用 IPv6 进行节点间通信(以及 CLI 工具)

除了客户端连接独占使用 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 工具发现它们应该使用哪个端口与之通信。它可以通过 ERL_EPMD_ADDRESS 环境变量配置为绑定到特定接口,这与 RabbitMQ 监听器类似。

# 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.0 版本(文本头部格式)和 2.0 版本(二进制头部格式)。

当连接通过代理(例如HAproxyAWS ELB)时,该协议可让 RabbitMQ 等服务器感知实际客户端 IP 地址。这使得运维人员更容易在管理 UI 或 CLI 工具中检查连接来源。

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

proxy_protocol = true

当代理协议启用时,客户端将无法直接连接到 RabbitMQ,除非它们自己支持该协议。因此,当此选项启用时,所有客户端连接都必须通过一个也支持该协议并配置为发送代理协议头部的代理。 HAproxyAWS ELB 文档解释了如何操作。

当代理协议启用并通过兼容代理连接时,客户端库无需进行任何操作或修改。通信对它们来说完全透明。

STOMPMQTT,以及 Web STOMPWeb MQTT 都有各自启用代理协议支持的设置。

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 之间。

为了获得最大的吞吐量,可以通过一组配置选项来增加缓冲区大小:

  • tcp_listen_options(用于 AMQP 0-9-1 和 AMQP 1.0)
  • mqtt.tcp_listen_options(用于 MQTT)
  • stomp.tcp_listen_options(用于 STOMP)

请注意,增加 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 资源量。
  • VM 配置允许的最大 Erlang 进程数。

打开文件句柄限制

重要

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

大多数操作系统限制了可以同时打开的文件句柄的数量。当操作系统进程(如 RabbitMQ 的 Erlang VM)达到限制时,它将无法打开任何新文件或接受任何更多 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 缓冲区大小:

  • tcp_listen_options(用于 AMQP 0-9-1 和 AMQP 1.0)
  • mqtt.tcp_listen_options(用于 MQTT)
  • stomp.tcp_listen_options(用于 STOMP)

减小 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 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),并加剧本节所述的问题。

检查连接和收集证据

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

内核将分配的最大文件数。可以使用 /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 也有自己的 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

设置为 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 Keepalive(见上文)。默认为 false。对于连接可能长时间空闲(至少 10 分钟)的环境很有用,尽管仍然建议使用心跳而不是此选项。

默认值

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

  • TCP 连接积压限制为 128 个连接。
  • Nagle 算法已禁用。
  • 服务器套接字 Linger 已启用,超时时间为 0。

心跳

RabbitMQ 支持的某些协议,包括 AMQP 0-9-1,支持心跳,这是一种更快地检测死去的 TCP 对等方的方法。有关更多信息,请参阅心跳指南

网络滴答时间

心跳用于检测客户端和 RabbitMQ 节点之间的对等方或连接故障。net_ticktime 为集群节点通信服务,但目的相同。低于 5(秒)的值可能导致误报,不建议使用。

TCP Keepalives

TCP 包含一种机制,其目的与消息协议中的心跳(也称为 keepalive)以及上述网络滴答时间相似:TCP Keepalives。由于默认设置不当,TCP Keepalives 通常无法按预期工作:检测死去的对等方需要很长时间(例如,一个小时或更长)。但是,通过调优,它们可以实现与心跳相同的功能,并清理陈旧的 TCP 连接,例如与选择不使用心跳的客户端(有意或无意)。

下面是一个 TCP Keepalives 的 sysctl 配置示例,它认为 TCP 连接在 70 秒后(在连接空闲 30 秒后每 10 秒进行 4 次尝试)是死掉的或不可达的。

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

TCP Keepalives 在 RabbitMQ 运维人员无法控制应用程序设置或使用的客户端库的环境中,可以作为一种有用的附加防御机制。

连接握手超时

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 查询:

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 工具是否应优先使用操作系统解析器、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.