连接
概述
本指南涵盖了与连接相关的各种主题,但网络调优或大多数与网络相关的主题除外。这些内容在网络和网络故障排除指南中介绍。
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 对操作员来说不太方便使用。
网络指南有一个专门介绍为大量并发连接进行调优的部分。它解释了如何减少每个连接的内存占用空间。
流控制
发布消息的连接可能会超过系统的其他部分,最可能是繁忙的队列和执行复制的队列。当发生这种情况时,流控制将应用于发布连接。仅消费消息的连接不受应用于发布者的流控制的影响。
因此,建议在可能的情况下,发布者和消费者使用单独的连接,以便消费者与可能应用于发布连接的潜在流控制隔离,从而影响手动消费者确认。
对于使用自动确认模式的较慢的消费者,当写入 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.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 客户端支持此功能。此功能在很大程度上是协议和客户端库特定的。
其他客户端可能认为网络故障恢复是应用程序的责任。在这种情况下,它们通常提供包括连接和拓扑恢复的示例。