原力注入

TCP Keepalive 指南

Image

什么是 TCP Keepalive?

TCP Keepalive 是一种 TCP 协议内置的探测机制,用于检测长时间未活动的连接是否仍然存活。当启用了 Keepalive 后,TCP 会在连接空闲一定时间后,定期向对端发送探测包,如果未收到对端的响应,则会尝试多次探测,最终关闭连接。

用途:

检测并清理死连接,防止资源长期占用。提高系统的可靠性,适用于长连接场景(如数据库连接、RPC 调用等)。快速感知网络断开,便于应用层及时处理。

2. TCP Keepalive 的工作流程

大致工作流程如下:

  • • 空闲时间检测:TCP 连接进入空闲状态超过 tcp_keepalive_time 后,开始发送探测包。

  • • 发送探测包:每隔 tcp_keepalive_intvl 秒发送一次探测包。

  • • 响应与失败处理:

    • • 如果对端响应,则认为连接正常;

    • • 如果连续发送 tcp_keepalive_probes 次探测包无响应,则认为连接已断开,关闭连接。

3. TCP Keepalive 参数详解

TCP Keepalive 的核心参数可以通过 /proc/sys/net/ipv4/文件调整:

参数名称默认值参数含义
tcp_keepalive_time7200 秒连接空闲多久后开始发送探测包(秒)
tcp_keepalive_intvl75 秒探测包之间的间隔时间(秒)
tcp_keepalive_probes9 次探测失败前最多发送的探测包数量

示例:调整 Keepalive 参数(以 60 秒空闲时间为例):

sysctl -w net.ipv4.tcp_keepalive_time=60
sysctl -w net.ipv4.tcp_keepalive_intvl=10
sysctl -w net.ipv4.tcp_keepalive_probes=3

4. TCP Keepalive 的启用方式

TCP Keepalive 功能的启用分为两层:应用层启用和系统层配置。

4.1 应用层显式启用

TCP Keepalive 功能必须由应用程序显式调用 SO_KEEPALIVE 选项开启,操作系统参数仅作为默认配置,服务于已启用的应用。

在应用层中,可通过以下代码启用 Keepalive:

int keepalive = 1; // 开启 Keepalive
int keepidle = 60; // 空闲 60 秒后开始探测
int keepintvl = 10; // 每隔 10 秒探测一次
int keepcnt = 3; // 最多探测 3 次

 // 设置 SO_KEEPALIVE
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));

 // 设置 TCP_KEEPIDLE
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle));

 // 设置 TCP_KEEPINTVL
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(keepintvl));

 // 设置 TCP_KEEPCNT
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt));

4.2 系统层参数配置

如果应用层未显式启用 TCP Keepalive(如没有设置 SO_KEEPALIVE),仅调整系统参数(如 net.ipv4.tcp_keepalive_time)是无效的。系统参数的作用是为已启用 Keepalive 的连接提供默认行为,不能全局强制启用 Keepalive。

5. 客户端与服务端的 Keepalive 配置

5.1 客户端 Socket 连接场景

客户端需要显式在程序中启用 TCP Keepalive,例如:

Socket socket = new Socket();
socket.setKeepAlive(true); // 启用 Keepalive

或通过框架提供的配置项(如 Netty、Spring 等)启用 Keepalive。

5.2 服务端 Socket 监听场景

服务端可以通过套接字监听程序设置 Keepalive,适用于清理死连接的场景:

ServerSocket serverSocket = new ServerSocket(port);
serverSocket.setReuseAddress(true); // 可选
serverSocket.setKeepAlive(true); // 启用 Keepalive

对于使用 Nginx、Apache 等 Web 服务器的场景,可以通过配置文件显式启用 Keepalive。例如:

keepalive_timeout 60;

6. 如何确认 TCP Keepalive 是否生效

6.1 检查套接字选项

使用 ss 或 netstat 查看连接是否启用了 Keepalive:

ss -4no state established | grep 2379
tcp    0      0      172.18.120.55:2379                 172.18.120.56:52104                 timer:(keepalive,19sec,0)
tcp    0      0      172.18.120.55:2379                 172.18.20.153:48184                 timer:(keepalive,14sec,0)
tcp    0      0      172.18.120.55:2379                 172.18.120.55:49988                 timer:(keepalive,6.493ms,0)
tcp    0      0      172.18.120.55:2379                 172.18.120.56:52176                 timer:(keepalive,3.549ms,0)
tcp    0      0      172.18.120.55:51590                172.18.20.153:2379                  timer:(keepalive,11sec,0)
tcp    0      0      172.18.120.55:49878                172.18.120.55:2379                  timer:(keepalive,9.693ms,0)
tcp    0      0      172.18.120.55:49564                172.18.20.153:2379                  timer:(keepalive,9.565ms,0)
tcp    0      0      172.18.120.55:2379                 172.18.120.56:50060                 timer:(keepalive,17sec,0)
tcp    0      0      172.18.120.55:2379                 172.18.120.55:48138                 timer:(keepalive,17sec,0)
tcp    0      0      172.18.120.55:37618                172.18.20.153:2379                  timer:(keepalive,2.845ms,0)
tcp    0      0      172.18.120.55:2379                 172.18.20.153:53632                 timer:(keepalive,18sec,0)

timer:(keepalive,19sec,0)

  • • 表示启用了 TCP Keepalive 功能,且计时器处于 Keepalive 状态。

  • • keepalive:当前连接处于 Keepalive 探测模式。

  • • 19sec:距离下一次 Keepalive 探测还有 19 秒。

  • • 0:表明当前尚未检测到丢包(即 Keepalive 探测未失败)。

netstat -4npo | grep -i established | head
tcp        0      0 172.18.120.55:2379      172.18.120.56:52104     ESTABLISHED 8072/etcd            keepalive (11.02/0/0)
tcp        0      0 172.18.120.55:2379      172.18.20.153:48184     ESTABLISHED 8072/etcd            keepalive (0.52/0/0)
tcp        0      0 172.18.120.55:2379      172.18.120.55:49988     ESTABLISHED 8072/etcd            keepalive (5.13/0/0)
tcp        0      0 172.18.120.55:2379      172.18.120.56:52176     ESTABLISHED 8072/etcd            keepalive (24.84/0/0)
tcp        0      0 172.18.120.55:51590     172.18.20.153:2379      ESTABLISHED 8804/kube-apiserver  keepalive (1.42/0/0)
tcp        0      0 172.18.120.55:49878     172.18.120.55:2379      ESTABLISHED 8804/kube-apiserver  keepalive (8.01/0/0)
tcp        0      0 172.18.120.55:49564     172.18.20.153:2379      ESTABLISHED 8804/kube-apiserver  keepalive (12.87/0/0)
tcp        0      0 172.18.120.55:2379      172.18.120.56:50060     ESTABLISHED 8072/etcd            keepalive (5.77/0/0)
tcp        0      0 172.18.120.55:2379      172.18.120.55:48138     ESTABLISHED 8072/etcd            keepalive (19.08/0/0)
tcp        0      0 172.18.120.55:37618     172.18.20.153:2379      ESTABLISHED 8804/kube-apiserver  keepalive (14.60/0/0)
...

keepalive (23.96/0/0):

  • • 表示当前 TCP 连接已启用 Keepalive 功能;

  • • 23.96: 距离下次发送 Keepalive 探测包的剩余时间(单位为秒);

  • • 0: 当前未检测到失败的探测包计数;

  • • 0: 累积的重传探测次数。

6.2 抓包验证

使用 tcpdump 检查 Keepalive 探测包:

tcpdump -i eth0 tcp port <port> and tcp[tcpflags] == tcp-ack

6.3 调试日志

检查应用日志中是否存在与 Keepalive 探测相关的异常或断开信息。

参考

TCP Keepalive HOWTO(https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/index.html):此文档描述了 Linux 内核中的 TCP keepalive 实现,介绍了总体概念并指出了系统配置和软件开发。