跳至主内容
版本:4.3

使用心跳和 TCP Keepalives 检测死掉的 TCP 连接

概述

网络故障的形式多种多样,有时非常隐蔽(例如高比例的数据包丢失)。当 TCP 连接中断时,操作系统检测到该状况通常需要较长时间(例如在 Linux 默认配置下约为 11 分钟)。AMQP 0-9-1 提供了一种心跳(heartbeat)功能,以确保应用层能够及时发现中断的连接(以及完全无响应的对端)。心跳还能防御某些网络设备,这些设备可能会在特定时间内没有活动时终止“空闲”的 TCP 连接。

TCP keepalives 是一项 TCP 协议栈功能,其目的类似,并且非常有用(可以与心跳结合使用),但在大多数操作系统和发行版中,需要进行内核调优才能发挥其实际作用。

心跳超时值

心跳超时(heartbeat timeout)值定义了 RabbitMQ 和客户端库在多长时间后应将对端的 TCP 连接视为不可达(断开)。此值在连接时由客户端和 RabbitMQ 服务器协商确定。必须配置客户端以请求心跳。

协商过程如下:服务器将建议其可配置的值,客户端将其与自身配置的值进行协调,并将最终结果发送回服务器。该值以为单位,RabbitMQ 建议的默认值为 60

警告

将心跳超时设置为过小的值可能会导致误报:即在连接并未真正断开的情况下将其视为不可用。

由 RabbitMQ 核心团队维护的 Java、.NET 和 Erlang 客户端使用以下协商算法:

  • 如果任一值为 0(见下文),则使用两者中的较大值。
  • 否则,使用两者中的较小值。

零值表示对端建议完全禁用心跳。要禁用心跳,双方都必须同意并使用值 0。除非确定环境中每台主机都使用了 TCP keepalives,否则强烈不建议这样做。

同样,也强烈不建议设置非常小的值

低超时值与误报

信息

对于大多数环境,5 到 20 秒范围内的值是最佳的。

由于瞬时网络拥塞、短时间的服务器流控等原因,将心跳超时值设置得太低可能会导致误报(即在连接并未真正断开的情况下将其视为不可用)。

在选择超时值时应考虑到这一点。

多年来从用户和客户端库维护者那里收到的反馈表明,低于 5 秒的值很有可能导致误报,而 1 秒或更低的值极有可能导致误报。对于大多数环境,5 到 20 秒范围内的值是最佳的。

心跳帧

心跳帧大约每 心跳超时 / 2 秒发送一次。该值有时被称为 心跳间隔(heartbeat interval)。如果错失两次心跳,则认为对端不可达。不同的客户端表现方式不同,但 TCP 连接都会被关闭。当客户端检测到 RabbitMQ 节点因心跳问题不可达时,需要重新连接。

切勿混淆超时值和间隔值。RabbitMQ 配置公开的是超时值,官方支持的客户端库也是如此。但是,某些客户端可能会公开间隔值,从而可能引起混淆。

任何流量(例如协议操作、已发布的消息、确认信息)都计入有效的心跳。客户端可以选择无论连接上是否有其他流量都发送心跳帧,但有些客户端只在必要时才这样做。

如何停用心跳

通过在连接时将客户端侧的超时间隔设置为 0(前提是服务器的心跳也已设置为零),可以停用心跳。

警告

除非确定环境中的每台主机(包括 RabbitMQ 节点和应用程序)都使用了 TCP keepalives,否则不建议停用心跳。

或者,可以在两端使用非常大的值(例如 1800 秒)来有效地停用心跳,因为帧的发送频率太低,不会产生实际影响。

除非改用具有足够低的非活动检测周期的 TCP keepalives,否则强烈不建议停用心跳。如果停用心跳,将很难及时发现对端不可用,这会给数据安全带来重大风险,特别是对于发布者而言。

使用 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 包含心跳功能,名称不同(称为“keepalives”)。RabbitMQ MQTT 插件完全支持此功能。

MQTT 中的 keepalives 是可选的。要启用它们,请在连接时设置 keepalive 间隔。有关示例,请查阅您的 MQTT 客户端文档。

Shovel 和 Federation 插件中的心跳

ShovelFederation 插件在底层向 RabbitMQ 节点打开 Erlang 客户端连接。因此,可以配置它们以使用所需的心跳值。

详情请参考 AMQP 0-9-1 URI 查询参数参考

TCP Keepalives

TCP 包含一种与消息协议中的心跳(又称 net tick timeout)目的类似的机制:TCP keepalives。由于默认配置不当,不能假定 TCP keepalives 适用于消息协议。但是,通过适当的调优,它们可以作为一种额外的防御机制,在无法让应用程序启用心跳或设置合理值的环境中使用。

在某些极少数情况下,如果仅靠心跳不足以满足要求(例如连接使用的协议本身没有心跳机制),则必须将 TCP keepalives 配置为使用合理的低超时值。

TCP keepalives 涵盖主机上的所有 TCP 连接,包括入站和出站连接。这使得它们在出站连接频繁变动的场景中非常有用,例如经常被停用和重新激活(重新启用)或中断的 ShovelFederation 插件链路。

TCP keepalives 也可以通过配置更低的系统特定值来代替心跳。在这种情况下,可以停用心跳。这种方法的主要优点是,无论使用何种协议和客户端库,机器上的所有 TCP 连接都将使用相同的值。

详情请参阅 网络指南

心跳与 TCP 代理

某些网络工具(HAproxy、AWS ELB)和设备(硬件负载均衡器)可能会在特定时间内没有活动时终止“空闲”的 TCP 连接。大多数情况下,这是不希望发生的。

当连接上启用了心跳时,会产生周期性的轻量级网络流量。因此,心跳具有保护可能长时间空闲的客户端连接免受代理和负载均衡器过早关闭的副作用。

使用 30 秒的心跳超时,连接将大约每 15 秒产生一次网络流量。5 到 15 秒范围内的活动足以满足大多数常用代理和负载均衡器的默认设置。另请参阅上面关于低超时和误报的部分。

排查活跃和失效的连接

RabbitMQ 节点会记录因心跳丢失而关闭的连接。所有官方支持的客户端库也会这样做。检查服务器和客户端日志将提供有价值的信息,并应作为故障排查的第一步。

可能需要检查节点打开的连接(入站或出站)、它们的状态、来源、用户名以及有效的心跳超时值。网络故障排查指南概述了可用于此操作的工具。

© . This site is unofficial and not affiliated with VMware.