连接
概述
本指南涵盖了与连接相关的各类主题,但不包括网络调优或大多数与网络相关的话题。这些内容已在 网络 (Networking) 和 网络故障排查 (Troubleshooting Networking) 指南中进行了说明。
RabbitMQ 支持多种协议
- 带有 扩展 的 AMQP 0-9-1
- AMQP 1.0
- RabbitMQ 流协议 (RabbitMQ Stream Protocol)
- MQTT 3.1、3.1.1 和 5.0
- STOMP 1.0 到 1.2 版本
本指南中的许多主题同样适用于所有协议。如果不适用,指南会尝试突出特定于协议的功能和实践。
通道 (Channels) 是 AMQP 0-9-1 中一个密切相关的概念,也在单独的指南中进行了介绍。
本指南涵盖
- 客户端如何使用 RabbitMQ 的基本原理
- 连接生命周期
- 如何加密客户端连接流量
- 如何让客户端提供连接名称以便于故障排查
- 连接事件日志
- 监控连接以及如何检测高连接变动 (high connection churn) 场景
- 维持大量并发连接
- TLS
- 流控制
- 连接异常(协议错误)
- 客户端属性与能力
- 网络故障恢复
以及其他与连接相关的主题。
基础知识
应用程序通过客户端库与 RabbitMQ 交互。许多编程语言和平台都提供了相应的客户端库。每种协议都有自己的一套客户端库。大多数客户端库都是开源的。
在本指南中,客户端库和使用它们的应用程序都被称为“客户端”。在需要区分两者的地方,会使用更具体的术语(例如“应用程序”)。
RabbitMQ 支持的所有协议均基于 TCP,并假设使用长连接以提高效率(不会为每个协议操作打开新连接)。一个客户端库连接对应一个 TCP 连接。为了使客户端成功连接,目标 RabbitMQ 节点必须允许在特定的协议端口上进行连接。
客户端连接并成功通过 RabbitMQ 节点认证后,即可发布和消费消息、定义拓扑,以及执行协议提供且客户端库与目标 RabbitMQ 节点均支持的其他操作。
由于连接旨在长期保持,客户端通常通过注册订阅来消费消息,并由服务器将消息推送给它们,而不是采用轮询方式。无法维持长连接的客户端可以使用专用代理来帮助减少连接变动。
当连接不再需要时,应用程序必须关闭它们以节省资源。未能做到这一点的应用程序可能会耗尽目标节点的资源。
操作系统对单个进程同时打开的 TCP 连接(套接字)数量有限制。此限制通常足以应付开发和某些 QA 环境。生产环境必须配置为使用更高的限制,以支持大量的并发客户端连接。
协议差异
不同的消息协议使用不同的端口。普通 TCP 连接和启用 TLS 的连接端口也不同。网络指南涵盖了 RabbitMQ 使用的所有端口,具体取决于启用的协议、是否使用 TLS 等。
AMQP 0-9-1
AMQP 0-9-1 提供了一种在单个 TCP 连接上进行多路复用的方法。这意味着应用程序可以在一个连接上打开多个被称为“通道 (channels)”的“轻量级连接”。AMQP 0-9-1 客户端在连接后会打开一个或多个通道,并在这些通道上执行协议操作(管理拓扑、发布、消费)。
AMQP 1.0
AMQP 1.0 提供了一种在单个 TCP 连接上进行多路复用的方法。这意味着应用程序可以在一个连接上打开多个被称为“会话 (sessions)”的“轻量级连接”。随后,应用程序会建立一个或多个链路 (links) 来发布和消费消息。
连接生命周期
为了与 RabbitMQ 交互,客户端必须首先打开连接。此过程涉及以下几个步骤:
- 应用程序配置其使用的客户端库以使用特定的连接端点(例如主机名和端口)
- 库将主机名解析为一个或多个 IP 地址
- 库打开到目标 IP 地址和端口的 TCP 连接
- 服务器接受 TCP 连接后,执行特定于协议的协商过程
- 服务器随后对客户端进行认证
- 客户端现在可以执行操作,每一个操作都会经过服务器的授权检查。
- 客户端在需要与 RabbitMQ 通信期间会一直保持连接
此流程在不同协议之间没有显著变化,但存在细微差别。
协议差异
AMQP 0-9-1
AMQP 0-9-1 具有一个包含连接和通道的模型。通道允许进行连接多路复用(在一个“物理”或 TCP 连接上拥有多个逻辑连接)。
一个连接上可同时打开的通道最大数量由客户端和服务器在连接时协商确定。客户端无法配置为允许超过服务器配置的最大值。
成功打开连接并进行认证后,应用程序打开一个或多个通道,并使用它们执行协议操作,例如定义拓扑、消费和发布消息。
AMQP 0-9-1 支持多种认证机制。虽然应用程序通常提供凭据对,但也可以使用 x509 证书和 PKI。
AMQP 1.0
AMQP 1.0 具有一个包含连接、会话和链路的模型。
成功打开连接并进行认证后,应用程序会打开一个或多个会话。然后,它将链路附加到会话以发布和消费消息。
MQTT
MQTT 连接遵循上述流程。MQTT 支持可选的认证。在使用时,RabbitMQ 会使用预先配置的凭据集。
STOMP
STOMP 连接遵循上述流程。
使用 TLS 加密连接流量
RabbitMQ 支持的所有协议都允许“明文”(未加密)流量,换句话说,TLS 不是强制要求的。然而,强烈建议在生产系统中使用 TLS,以防止流量嗅探和中间人攻击。
使用未加密连接的应用程序也会以“明文”形式发送凭据。某些安全扫描程序会将其报告为“AMQP 明文认证”。解决方案是对这些客户端连接使用 TLS。
欲了解更多信息,请参阅 TLS 专题指南:客户端连接 TLS、使用 TLS 保护集群内通信以及 TLS 故障排查。
日志记录
RabbitMQ 会记录所有至少发送了 1 字节数据的入站客户端连接。没有任何活动的连接将不会被记录。这是为了防止 TCP 负载均衡器的健康检查淹没日志。
成功的认证、正常及异常的连接关闭也将被记录。
此主题在日志指南中有更详细的介绍。
监控
当前打开的客户端连接数以及连接打开/关闭的速率是系统的重要指标,应当进行监控。监控它们有助于检测消息系统中常见的许多问题:
- 连接泄漏
- 高连接变动
这两个问题最终都会导致节点资源耗尽。
连接泄漏F
连接泄漏是指应用程序反复打开连接而不关闭它们,或者至少只关闭其中一部分的状况。
连接泄漏最终会耗尽节点(或多个目标节点)的文件句柄,这意味着任何新的入站客户端、对等节点或 CLI 工具连接都将被拒绝。并发连接数的积累也会增加节点的内存消耗。
相关指标
管理 UI 提供了整个集群打开的连接总数图表。

监控图表上的连接泄漏可以表现为客户端连接数单调增长。
还可以查看特定节点拥有多少文件句柄和套接字,这对于确定连接泄漏也很有用。下图展示了一个节点上保持非常稳定的套接字数量。

下图展示了连接数在下降后呈现出单调增长的趋势。

如果一个节点使用的套接字数量持续不断地增长,这很可能表明其中一个应用程序存在连接泄漏。
一些客户端库(例如 Java 客户端)会公开包括当前打开连接数在内的指标。对应用程序的连接指标进行绘图和监控是识别哪个应用程序泄漏连接或以次优方式使用连接的最佳方法。
在许多使用长连接且不泄漏的应用程序中,连接数会在应用程序启动时增长,然后趋于平缓(保持基本稳定,波动很小)。
管理 UI 从 RabbitMQ 3.7.9 开始提供新打开连接速率的图表。下方是一个显示连接速率相当低的图表。

高连接变动
当一个系统的新增连接速率和关闭连接速率持续处于高位时,称该系统具有高连接变动。这通常意味着应用程序使用了短连接。虽然在某些工作负载下这种情况难以避免,但如果可能,应该使用长连接。
RabbitMQ 会收集有关连接变动的指标,并通过 Prometheus 和 Grafana 以及 管理 UI 的变动速率图表展示出来。下图显示了一个在给定时间段内打开和关闭的连接数相当的较低连接变动。

虽然连接和断开连接的速率取决于系统,但持续超过每秒 100 次的速率很可能表明一个或多个应用程序采用了次优的连接管理方法,通常值得调查。

一些客户端和运行时环境(特别是 PHP)不使用长连接,因此高连接变动速率在它们身上是预料之中的。应配合这些客户端使用专用代理,以减轻它们自然产生的变动。
经历高连接变动的环境需要调整 TCP 栈以避免在高变动下出现资源耗尽。
资源使用
每个连接都会消耗内存和目标 RabbitMQ 节点上的一个文件句柄。
大部分内存是由连接的 TCP 缓冲区使用的。它们的大小可以显著减小,这能显著节省每个连接的内存消耗,但会以相应降低连接吞吐量为代价。
RabbitMQ 节点可以打开的最大文件句柄数受内核限制,为了支持大量连接,必须提高此限制。
支持大量连接
在某些环境中,拥有大量并发连接的客户端是很自然的。例如,涉及大量硬件客户端的系统(物联网,即 IoT 工作负载)从第一天起就可能拥有数千个客户端。
由于连接会消耗资源,维持大量并发连接需要减少资源消耗,或配置更多的资源或节点。在实践中,这两种选项通常结合使用。
大量的并发连接会产生许多指标(统计)发射事件。即使连接大多处于空闲状态,这也会增加 CPU 消耗。为了减少此开销,可以使用 collect_statistics_interval 键来增加统计收集间隔。
# sets the interval to 60 seconds
collect_statistics_interval = 60000
默认值为 5 秒(5000 毫秒)。
将间隔值增加到 30-60 秒将减少 CPU 开销和峰值内存消耗。但这样做有一个缺点:使用上述示例中的值,相关实体的指标将每 60 秒刷新一次。
在外部监控的生产系统中,这完全是合理的,但会使管理 UI 对于操作员来说使用起来不太方便。
网络指南有一个专门针对调整以支持大量并发连接的章节。它解释了如何减少每个连接的内存占用。
流量控制
发布消息的连接可能会超过系统其他部分的处理能力,最常见的情况是繁忙的队列和执行复制的队列。当这种情况发生时,会对发布连接应用流量控制。仅消费消息的连接不受应用于发布者的流量控制的影响。
因此,建议尽可能让发布者和消费者使用独立的连接,以便消费者与可能应用于发布连接的流量控制隔离开来,从而避免影响手动消费者确认。
对于使用自动确认模式的较慢消费者,连接和通道在写入 TCP 套接字时极有可能遇到流量控制。
监控系统可以收集处于流量控制状态的连接数指标。经常遇到流量控制的应用程序可以考虑使用单独的连接来发布和消费,以避免流量控制影响非发布操作(例如队列管理)。
错误处理和协议异常
连接可能会失败或无法满足客户端操作的要求。此类场景被称为错误或协议异常。它们可能指示瞬态状况(例如资源被锁定)、语义问题或协议实现问题(例如分帧错误)。
消息协议中的大多数错误被认为是不可恢复的。请注意,协议错误与网络连接故障是不同的。
协议差异
AMQP 0-9-1
在 AMQP 0-9-1 中,连接错误用于传达不可恢复的(“硬”)错误,例如分帧不正确或连接状态违规。例如,如果使用相同的 ID(编号)打开了多次通道。在向客户端发送错误后,连接即被关闭。
可以纠正和重试的错误则使用通道异常(“软错误”)进行传达。
AMQP 1.0
在 AMQP 1.0 中,大多数错误属于会话错误或链路错误。会话错误是不可恢复的,会导致检测到该错误的对等方收到的所有操作在会话终止前都被丢弃。
链路错误仅限于特定的链路。由于链路可以在不影响其会话的情况下附加和重新附加,在实践中,应用程序可以在纠正根本原因(如果可能)后重试失败的操作。
STOMP
在 STOMP 中,服务器通过发送 ERROR 帧并关闭 TCP 连接来传达错误。该帧将在 message 字段中包含错误信息。
MQTT 3.1
在 MQTT 3.1 中,服务器向客户端传达错误的方式有限。主要方式是关闭客户端的 TCP 连接。这对开发人员来说提供的上下文很少,可见性也有限。这是 MQTT 3.1 设计的一个基本局限性。
通常情况下,MQTT 客户端被配置为自动重新连接并重试操作,这可能会导致触发错误的循环、连接风暴以及惊群问题的变体。
因此,对于 MQTT,检查服务器日志和监控连接显得尤为重要。
客户端提供的连接名称
RabbitMQ 节点关于其客户端的信息有限:
- 它们的 TCP 端点(源 IP 地址和端口)
- 使用的凭据
仅凭这些信息就可能在识别应用程序和实例时遇到问题,尤其是在凭据可以共享,客户端通过负载均衡器连接,但 Proxy Protocol 无法启用时。
为了更容易在服务器日志和管理 UI 中识别客户端,包括 RabbitMQ Java 客户端在内的 AMQP 0-9-1 客户端连接可以提供一个自定义标识符。如果设置了,该标识符将在日志条目和管理 UI 中提及。该标识符被称为客户端提供的连接名称。该名称可用于识别应用程序或应用程序内的特定组件。名称是可选的;但强烈建议开发人员提供一个,因为它会显著简化某些操作任务。
连接名称必须使用客户端能力表中的 "connection_name" 字段指定。一些客户端库(例如 Java 和 .NET)提供了更便捷的方式来设置自定义连接名称。
客户端和服务器能力
一些协议(即 AMQP 0-9-1)允许客户端和服务器在打开连接时声明它们的能力。这可以被视为一个可选功能表,特定版本的 RabbitMQ 和客户端库可能会支持也可能不支持。这种机制类似于 RabbitMQ 节点使用的功能标志 (feature flags),用于确定所有集群成员支持的功能集,以及新成员是否能够加入集群。
这些能力键的值通常为布尔值,表示是否支持该能力,但可能会根据能力的性质而有所不同。
例如,RabbitMQ 节点呈现给客户端的能力表可能如下所示(此处呈现的格式可视为伪代码,因为实际的表编码是二进制格式,对人类并不友好):
{ "product" = (longstr) "RabbitMQ",
"platform" = (longstr) "Erlang/OTP",
"information" = (longstr) "Licensed under the MPL 2.0. Website: https://rabbitmq.cn",
"capabilities" = (table) { "exchange_exchange_bindings" = (bool) true,
"consumer_cancel_notify" = (bool) true,
"basic.nack" = (bool) true,
"publisher_confirms" = (bool) true },
"version" = (longstr) "3.12.10" }
客户端的能力表是可选的:不呈现此类表并不妨碍客户端使用交换机到交换机绑定等扩展功能。然而,在某些情况下(例如消费者取消通知),客户端必须呈现相关的能力,否则 RabbitMQ 节点将无法知道客户端能够接收这些额外的通知。
网络连接故障恢复
客户端的 TCP 连接可能会失败或经历严重的数据包丢失,从而导致 RabbitMQ 节点将其视为不可用。
一些客户端库提供了自动从网络连接故障中恢复的机制。例如,RabbitMQ Java 客户端和 RabbitMQ .NET 客户端支持此功能。此功能很大程度上是协议和客户端库特定的。
其他客户端可能认为网络故障恢复是应用程序的责任。在这种情况下,它们通常会提供包含连接和拓扑恢复示例的代码。