TCP KeepAlive

TCP Keepalive的起源

TCP 协议中有长连接和短连接之分。短连接环境下,数据交互完毕后,主动释放连接。

长连接的环境下,进行一次数据交互后,很长一段时间内无数据交互时,客户端可能意外断电、死机、崩溃、重启,还是中间路由网络无故断开,这些 TCP 连接并未来得及正常释放,那么,连接的另一方并不知道对端的情况,它会一直维护这个连接,长时间的积累会导致非常多的半打开连接,造成端系统资源的消耗和浪费,且有可能导致在一个无效的数据链路层面发送业务数据,结果就是发送失败。所以服务器端要做到快速感知失败,减少无效链接操作,这就有了 TCP 的 Keepalive(保活探测)机制。

TCP Keepalive工作原理

当一个 TCP 连接建立之后,启用 TCP Keepalive 的一端便会启动一个计时器,当这个计时器数值到达 0 之后(也就是经过 tcp_keep-alive_time 时间后,这个参数之后会讲到),一个 TCP 探测包便会被发出。这个 TCP 探测包是一个纯 ACK 包(规范建议,不应该包含任何数据,但也可以包含 1 个无意义的字节,比如 0x0。),其 Seq 号与上一个包是重复的,所以其实探测保活报文不在窗口控制范围内。

如果一个给定的连接在两小时内(默认时长)没有任何的动作,则服务器就向客户发一个探测报文段,客户主机必须处于以下 4 个状态之一:

  1. 客户主机依然正常运行,并从服务器可达。客户的 TCP 响应正常,而服务器也知道对方是正常的,服务器在两小时后将保活定时器复位。
  2. 客户主机已经崩溃,并且关闭或者正在重新启动。在任何一种情况下,客户的 TCP 都没有响应。服务端将不能收到对探测的响应,并在 75 秒后超时。服务器总共发送 10 个这样的探测 ,每个间隔 75 秒。如果服务器没有收到一个响应,它就认为客户主机已经关闭并终止连接。
  3. 客户主机崩溃并已经重新启动。服务器将收到一个对其保活探测的响应,这个响应是一个复位,使得服务器终止这个连接。
  4. 客户机正常运行,但是服务器不可达,这种情况与 2 类似,TCP 能发现的就是没有收到探测的响应。

对于 linux 内核来说,应用程序若想使用 TCP Keepalive,需要设置 SO_KEEPALIVE 套接字选项才能生效。有三个重要的参数:

  1. tcp_keepalive_time,在 TCP 保活打开的情况下,最后一次数据交换到 TCP 发送第一个保活探测包的间隔,即允许的持续空闲时长,或者说每次正常发送心跳的周期,默认值为 7200s(2h)。
  2. tcp_keepalive_probes 在 tcp_keepalive_time 之后,没有接收到对方确认,继续发送保活探测包次数,默认值为 9(次)。
  3. tcp_keepalive_intvl,在 tcp_keepalive_time 之后,没有接收到对方确认,继续发送保活探测包的发送频率,默认值为 75s。

其他编程语言有相应的设置方法,这里只谈 linux 内核参数的配置。例如 C 语言 中的 setsockopt() 函数,javaNetty 服务器框架中也提供了相关接口。

TCP Keepalive作用

  1. 探测连接的对端是否存活

    在应用交互的过程中,可能存在以下几种情况:

    • 客户端或服务器意外断电,死机,崩溃,重启。
    • 中间网络已经中断,而客户端与服务器并不知道。

    利用保活探测功能,可以探知这种对端的意外情况,从而保证在意外发生时,可以释放半打开的 TCP 连接。

  2. 防止中间设备因超时删除连接相关的连接表

    中间设备如防火墙等,会为经过它的数据报文建立相关的连接信息表,并为其设置一个超时时间的定时器,如果超出预定时间,某连接无任何报文交互的话,

    中间设备会将该连接信息从表中删除,在删除后,再有应用报文过来时,中间设备将丢弃该报文,从而导致应用出现异常。

TCP Keepalive可能导致的问题

Keepalive 技术只是 TCP 技术中的一个可选项。因为不当的配置可能会引起一些问题,所以默认是关闭的。可能导致下列问题:

  1. 在短暂的故障期间,Keepalive 设置不合理时可能会因为短暂的网络波动而断开健康的 TCP 连接。
  2. 需要消耗额外的宽带和流量。
  3. 在以流量计费的互联网环境中增加了费用开销。

TCP Keepalive与HTTP Keep-Alive关系

很多人会把 TCP Keepalive 和 HTTP Keep-Alive 这两个概念搞混淆。这里简单介绍下 HTTP Keep-Alive。

在 HTTP/1.0 中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次 HTTP 操作,就建立一次连接,但任务结束就中断连接。如果客户端浏览器访问的某个 HTML 或其他类型的 Web 页中包含有其他的 Web 资源,如 JavaScript 文件、图像文件、CSS 文件等;当浏览器每遇到这样一个 Web 资源,就会建立一个 HTTP 会话。

但从 HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的 HTTP 协议,会在响应头加上 Connection、Keep-Alive 字段。

HTTP 协议的 Keep-Alive 意图在于 TCP 连接复用,同一个连接上串行方式传递请求-响应数据;TCP 的 Keepalive 机制意图在于探测连接的对端是否存活。