使用心跳检测和 TCP Keepalive 检测死 TCP 连接
概述
网络可能以多种方式发生故障,有时非常微妙(例如,高数据包丢失率)。操作系统需要相当长的时间(例如,在 Linux 上使用默认配置大约 11 分钟)才能检测到中断的 TCP 连接。AMQP 0-9-1 提供了心跳功能,以确保应用层及时发现中断的连接(以及完全无响应的对等方)。心跳还可以防御某些网络设备,这些设备可能会在 TCP 连接在一段时间内没有活动时终止“空闲”的 TCP 连接。
TCP keepalive 是一种 TCP 堆栈功能,其目的类似,并且可能非常有用(可能与心跳结合使用),但需要内核调整才能在大多数操作系统和发行版中实用。
心跳超时值
heartbeat timeout
值定义了 RabbitMQ 和客户端库应将对等 TCP 连接视为不可达(关闭)的时间段。此值在客户端和 RabbitMQ 服务器连接时协商确定。客户端必须配置为请求心跳。
协商过程如下:服务器将建议其可配置值,客户端将协调其配置值,并将结果值发回。该值以秒为单位,RabbitMQ 建议的默认值为 60
。
将心跳超时值设置为非常低的值可能会导致误报:连接对等方被认为不可用,但实际上并非如此
RabbitMQ 核心团队维护的 Java、.NET 和 Erlang 客户端使用以下协商算法
- 如果任一值为
0
(见下文),则使用两个值中较大的值 - 否则,使用两个值中较小的值
零值表示对等方建议完全禁用心跳。要禁用心跳,双方都必须选择加入并使用值 0
。强烈建议不要这样做,除非已知环境在每台主机上都使用 TCP keepalive。
非常低的值也强烈建议不要使用。
低超时值和误报
对于大多数环境,5 到 20 秒范围内的值是最佳的
将心跳超时值设置得太低可能会导致误报(对等方被认为不可用,但实际上并非如此),这归因于瞬时网络拥塞、短暂的服务器流控制等等。
在选择超时值时应考虑这一点。
来自用户和客户端库维护人员的多年反馈表明,低于 5 秒的值很可能导致误报,而 1 秒或更低的值非常可能导致误报。对于大多数环境,5 到 20 秒范围内的值是最佳的。
心跳帧
心跳帧大约每 heartbeat timeout / 2
秒发送一次。此值有时称为 heartbeat interval
。在错过两次心跳后,对等方被认为不可达。不同的客户端对此表现不同,但 TCP 连接将被关闭。当客户端检测到 RabbitMQ 节点由于心跳而不可达时,它需要重新连接。
重要的是不要将超时值与间隔值混淆。RabbitMQ 配置 公开了超时值,官方支持的客户端库也是如此。但是,某些客户端可能会公开间隔,这可能会引起混淆。
任何流量(例如,协议操作、发布的消息、确认)都算作有效的心跳。客户端可以选择发送心跳帧,无论连接上是否有任何其他流量,但有些客户端仅在必要时才这样做。
如何禁用心跳
可以通过在连接时在客户端将超时间隔设置为 0
来禁用心跳,前提是服务器心跳也已设置为零。
不建议禁用心跳,除非已知环境在每台主机(RabbitMQ 节点和应用程序)上都使用 TCP keepalive
或者,可以在两端都使用非常高的值(例如,1800 秒)来有效地禁用心跳,因为帧传递频率太低,无法产生实际影响。
除非使用 TCP keepalive 并具有适当低的非活动检测周期,否则强烈不建议禁用心跳。如果禁用心跳,将使及时检测对等方不可用变得不太可能,这将对数据安全构成重大风险,特别是对于 发布者。
在 Java 客户端中启用心跳
要在 Java 客户端中配置心跳超时,请在创建连接之前使用 ConnectionFactory#setRequestedHeartbeat
设置它
ConnectionFactory cf = new ConnectionFactory();
// set the heartbeat timeout to 60 seconds
cf.setRequestedHeartbeat(60);
请注意,如果 RabbitMQ 服务器配置了非零心跳超时(这是默认值),则客户端只能降低该值,而不能增加它。
在 .NET 客户端中启用心跳
要在 .NET 客户端中配置心跳超时,请在创建连接之前使用 ConnectionFactory.RequestedHeartbeat
设置它
var cf = new ConnectionFactory();
// set the heartbeat timeout to 60 seconds
cf.RequestedHeartbeat = TimeSpan.FromSeconds(60);
STOMP 中的心跳
STOMP 1.2 包括心跳。在 STOMP 中,心跳超时可以是不对称的:也就是说,客户端和服务器可以使用不同的值。RabbitMQ STOMP 插件完全支持此功能。
STOMP 中的心跳是可选的。要启用它们,请在连接时使用 heart-beat
标头。有关示例,请参阅 STOMP 规范。
MQTT 中的心跳
MQTT 包括心跳,但名称不同(“keepalive”)。RabbitMQ MQTT 插件完全支持此功能。
MQTT 中的 Keepalive 是可选的。要启用它们,请在连接时设置 keepalive
间隔。有关示例,请查阅您的 MQTT 客户端的文档。
Shovel 和 Federation 插件中的心跳
Shovel 和 Federation 插件在后台打开到 RabbitMQ 节点的 Erlang 客户端连接。因此,可以将它们配置为使用所需的心跳值。
有关详细信息,请参阅 AMQP 0-9-1 URI 查询参数参考。
TCP Keepalive
TCP 包含一种机制,其目的与消息传递协议中的心跳(又名 keepalive)机制和上面介绍的网络心跳超时类似:TCP keepalive。由于默认值不充分,因此不能假定 TCP keepalive 适用于消息传递协议。但是,通过适当的调整,它们可以在应用程序无法启用心跳或使用合理值的环境中用作额外的防御机制。
在某些罕见的情况下,当仅心跳不足时(例如,当涉及的连接使用某种没有心跳机制的协议时),必须将 TCP keepalive 配置为使用合理低的超时值。
TCP keepalive 涵盖主机上的所有 TCP 连接,包括入站和出站连接。这使得它们在出站连接频繁变动的场景中很有用,例如,经常停用和重新激活(重新启用)或中断的 Shovel 或 Federation 插件链接。
TCP keepalive 也可以通过将它们配置为降低系统特定的值来代替心跳。在这种情况下,可以禁用心跳。这种方法的主要好处是,机器上的所有 TCP 连接都将使用相同的值,而与使用的协议和客户端库无关。
有关详细信息,请参阅 网络指南。
心跳和 TCP 代理
某些网络工具(HAproxy、AWS ELB)和设备(硬件负载均衡器)可能会在 TCP 连接在一段时间内没有活动时终止“空闲”的 TCP 连接。大多数情况下,这是不希望发生的。
当连接上激活心跳时,会导致周期性的轻量网络流量。因此,心跳具有保护客户端连接的副作用,这些客户端连接可能会空闲一段时间,从而防止被代理和负载均衡器过早关闭。
心跳超时为 30 秒时,连接将大约每 15 秒产生周期性网络流量。5 到 15 秒范围内的活动足以满足大多数流行的代理和负载均衡器的默认值。另请参阅上面关于低超时和误报的部分。
排查活动和失效连接问题
RabbitMQ 节点将 记录由于心跳丢失而关闭的连接。所有官方支持的客户端库也将如此。检查服务器和客户端日志将提供有价值的信息,并且应该是第一个故障排除步骤。
可能需要检查打开到节点或从节点打开的连接、其状态、来源、用户名和有效的心跳超时值。网络故障排除 指南概述了可用于帮助解决此问题的工具。