连接
概述
本指南涵盖与连接相关的各种主题,但不包括网络调优或大多数网络相关主题。这些主题已在 网络 和 网络故障排除 指南中进行了介绍。
RabbitMQ 支持多种协议
- 带有 扩展 的 AMQP 0-9-1
- AMQP 1.0
- RabbitMQ 流协议
- MQTT 3.1、3.1.1 和 5.0
- STOMP 1.0 至 1.2
本指南中的许多主题同样适用于所有协议。当情况并非如此时,本指南会尝试突出显示特定于协议的功能和实践。
在 AMQP 0-9-1 中,通道 是一个密切相关的概念,它也在单独的指南中进行了介绍。
本指南涵盖
- 客户端如何使用 RabbitMQ 的 基本知识
- 连接生命周期
- 如何 加密客户端连接上的流量
- 如何 客户端提供连接名称 以便更轻松地进行故障排除
- 连接 事件日志记录
- 监控 连接以及如何检测 高连接流失 场景
- 维持 大量并发连接
- TLS
- 流控制
- 连接异常 (协议错误)
- 客户端属性和功能
- 网络故障恢复
以及其他与连接相关的主题。
基础知识
应用程序通过客户端库与 RabbitMQ 进行交互。有适用于许多编程语言和平台的 客户端库。每种协议都有自己的一组客户端库。大多数客户端库都是开源的。
本指南中,“客户端”既指客户端库,也指使用它们的应用程序。当存在差异很重要时,我们会使用更具体的术语(例如,“应用程序”)。
RabbitMQ 支持的所有协议都基于 TCP,并且为了效率,假定使用长连接(每个协议操作不会打开新连接)。一个客户端库连接使用一个 TCP 连接。为了使客户端能够成功连接,目标 RabbitMQ 节点必须允许在 特定协议端口 上进行连接。
在客户端 连接 并成功通过 RabbitMQ 节点进行身份验证后,它可以发布和消耗消息、定义拓扑并执行协议提供的、并由客户端库和目标 RabbitMQ 节点都支持的其他操作。
由于连接旨在保持长连接,因此客户端通常通过注册订阅并接收消息(推送)而不是轮询来 消耗消息。无法保持长连接的客户端可以使用 特殊代理 来帮助减少 连接流失。
当连接不再需要时,应用程序必须关闭它们以节省资源。未能这样做的应用程序存在最终耗尽其目标节点的资源的风险。
操作系统对单个进程可以同时打开的 TCP 连接(套接字)数量有限制。这个限制通常足以满足开发和一些 QA 环境。 生产环境 必须配置为使用更高的限制,以支持更多并发客户端连接。
协议差异
不同的消息传递协议使用不同的端口。纯 TCP 和启用 TLS 的连接的端口也不同。 网络指南 涵盖了 RabbitMQ 使用的所有端口,具体取决于启用的协议、是否使用 TLS 等。
AMQP 0-9-1
AMQP 0-9-1 提供了一种在单个 TCP 连接上多路复用连接的方法。这意味着一个应用程序可以在一个连接上打开多个称为通道的“轻量级连接”。 AMQP 0-9-1 客户端在 连接 后打开一个或多个通道,并在通道上执行协议操作(管理拓扑、发布、消耗)。
AMQP 1.0
AMQP 1.0 提供了一种在单个 TCP 连接上多路复用连接的方法。这意味着一个应用程序可以在一个连接上打开多个称为会话的“轻量级连接”。然后,应用程序会设置一个或多个链接来发布和消耗消息。
连接生命周期
为了使客户端能够与 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 负载均衡器健康检查导致日志泛滥。
成功的身份验证、正常和意外的连接关闭也将被记录。
此主题在 日志记录指南 中有更详细的介绍。
监控
当前打开的客户端连接数以及连接打开/关闭速率是系统的重要指标,应该进行 监控。监控它们将有助于检测消息传递系统中常见的一些问题
- 连接泄露
- 高连接流失
这两个问题最终都会导致节点 资源 耗尽。
连接泄露
连接泄露是指应用程序反复打开连接而不关闭它们,或者至少只关闭其中一部分。
连接泄露最终会耗尽节点的 文件句柄(或多个目标节点),这意味着任何新的入站客户端、对等方或 CLI 工具连接都将被拒绝。并发连接数量的增加也会增加节点的内存消耗。
相关指标
管理 UI 提供了整个集群打开的总连接数的图表

监控图表中的连接泄露可以识别为客户端连接数量单调增长。
还可以查看特定节点拥有多少文件句柄和套接字,这在确定连接泄露方面也很有用。以下图表展示了节点上打开的套接字数量非常稳定

此图表展示了在下降后连接数量单调增长

如果节点使用的套接字数量不断增加,则很可能是应用程序中的连接泄露的迹象。
某些客户端库,例如 Java 客户端,公开了包括当前打开连接数量在内的指标。围绕连接的应用程序指标的图表和监控是识别哪些应用程序泄露连接或以次优方式使用它们的最佳方法。
在许多使用长连接且不发生泄露的应用程序中,连接数量会在应用程序启动时增长,然后趋于稳定(保持大部分稳定,波动很小)。
自 RabbitMQ 3.7.9 起,管理 UI 提供了新连接打开速率的图表。下面是一个展示了相当低的连接打开速率的图表

高连接流失
当一个系统的连接打开速率持续很高且连接关闭速率持续很高时,就说它存在高连接流失。这通常意味着应用程序使用了短连接。虽然对于某些工作负载来说,这种情况很难避免,但应尽可能使用长连接。
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 中识别客户端,AMQP 0-9-1 客户端连接(包括 RabbitMQ Java 客户端)可以提供自定义标识符。如果设置了,标识符将在日志条目和管理 UI 中提及。该标识符称为 **客户端提供的连接名称**。该名称可用于标识应用程序或应用程序内的特定组件。该名称是可选的;但是,强烈建议开发人员提供一个,因为它将大大简化某些操作任务。
连接名称必须使用 客户端功能表 中的 "connection_name" 字段进行指定。某些客户端库,例如 Java 和 .NET,提供了更便捷的方式来设置连接的自定义名称。
客户端和服务器功能
某些协议,特别是 AMQP 0-9-1,允许客户端和服务器在打开连接时表达它们的功能。这可以被认为是特定版本的 RabbitMQ 和客户端库可能支持或不支持的可选功能表。此机制类似于 RabbitMQ 节点使用的 功能标志,用于确定所有集群成员支持的功能集,以及新成员是否能够加入集群。
这些功能键的值通常是布尔值,表示该功能是否受支持,但可能因功能性质而异。
例如,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 客户端 支持此类功能。此功能在很大程度上是特定于协议和客户端库的。
其他客户端可能认为网络故障恢复是应用程序的责任。在这种情况下,它们通常提供包括连接和拓扑恢复在内的示例。