连接
概述
本指南涵盖了与连接相关的各种主题,但网络调优或大多数网络相关主题除外。这些内容在网络和网络故障排除指南中进行了介绍。
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 客户端,公开了指标,包括当前打开的连接数量。对围绕连接的应用程序指标进行图表绘制和监控是识别哪些应用程序泄漏连接或以次优方式使用连接的最佳方法。
在许多使用长期连接且不泄漏连接的应用程序中,连接数量在应用程序启动时增长,然后趋于稳定(保持相对稳定,波动很小)。
管理 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 对操作员来说不太方便使用。
网络指南有一个专门介绍调整大量并发连接的部分。它解释了如何减少每个连接的内存占用。
TLS
客户端连接可以使用 TLS 加密。TLS 也可以用作身份验证客户端的补充或主要方式。在TLS 指南中了解更多信息。
流量控制
发布消息的连接可能会超过系统的其他部分,最可能是繁忙的队列和执行复制的队列。发生这种情况时,将对发布连接应用流量控制。仅消费消息的连接**不受**应用于发布者的流量控制的影响。
因此,建议在可能的情况下,发布者和消费者使用单独的连接,以便消费者与可能应用于发布连接的潜在流量控制隔离,从而影响手动消费者确认。
对于使用自动确认模式的较慢的消费者,连接和通道在写入 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 客户端通常配置为自动重新连接和重试操作,这可能会导致错误触发循环、连接风暴和惊群问题的变体。
客户端提供的连接名称
RabbitMQ 节点对客户端的信息有限
- 它们的 TCP 端点(源 IP 地址和端口)
- 使用的凭据
仅凭这些信息就可能难以识别应用程序和实例,尤其是在凭据可以共享并且客户端通过负载均衡器连接但代理协议无法启用时。
为了更容易地在服务器日志和管理 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.org.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 客户端 支持此功能。此功能在很大程度上是特定于协议和客户端库的。
其他客户端可能认为网络故障恢复是应用程序的责任。在这种情况下,它们通常提供包含连接和拓扑恢复的示例。