网络协议2a-TCP的KeepAlive

TCP 协议的KeepAlive

KeepAlive 并不是 TCP 协议规范的一部分,但在几乎所有的 TCP/IP 协议栈(不管是 Linux 还是 Windows)中,都实现了 KeepAlive 功能。
参考 [RFC1122 #TCP Keep-Alives] @ref

TCP 协议栈与 KeepAlive 相关的参数有:

  • tcp_keepalive_time: KeepAlive 的空闲时长,或者说每次正常发送心跳的周期,默认值为 7200 s(2 小时)@doubt
  • tcp_keepalive_intvl: KeepAlive 探测包的发送间隔,默认值为 75s
  • tcp_keepalive_probes: 在 tcp_keepalive_time 之后,没有接收到对方确认,继续发送保活探测包次数,默认值为 9(次)

以上参数都可以通过 sysctl 命令修改内核参数实现修改: sysctl -w net.ipv4.tcp_keepalive_time = 7500,
更多 sysctl 使用参考 [[../21.Operating-System/Linux.04a.Sysctl]]

KeepAlive 默认情况下是关闭的,可以被上层应用开启和关闭, 下面介绍在 Java、C 语言和 Nginx 中如何设置 KeepAlive

代码和应用程序如何设置 Keepalive

Java(Netty)设置 KeepAlive

ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.handler(new LoggingHandler(LogLevel.INFO));

Java 程序只能做到设置 SO_KEEPALIVE 选项,其他配置项只能依赖于 sysctl 配置,系统进行读取。

C 语言 socket 编程设置 KeepAlive

setsockopt 函数原型:

#include <sys/socket.h>

int setsockopt(int socket, int level, int option_name,
const void *option_value, socklen_t option_len);

How to use:

int socket(int domain, int type, int protocol)
{
int (*libc_socket)(int, int, int);
int s, optval;
char *env;

*(void **)(&libc_socket) = dlsym(RTLD_NEXT, "socket");
if(dlerror()) {
errno = EACCES;
return -1;
}

if((s = (*libc_socket)(domain, type, protocol)) != -1) {
if((domain == PF_INET) && (type == SOCK_STREAM)) {
if(!(env = getenv("KEEPALIVE")) || strcasecmp(env, "off")) {
optval = 1;
} else {
optval = 0;
}
if(!(env = getenv("KEEPALIVE")) || strcasecmp(env, "skip")) {
setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
}
// ...
}
}
return s;
}

代码摘取自 libkeepalive 源码,C 语言可以设置更为详细的 TCP 内核参数

Nginx 配置 KeepAlive

Nginx 配置里有两处 KeepAlive, 含义不同:

  • listen 下的 so_keepalive=30m::10: 设置 TCP_KEEPIDLE 为 30 分钟, TCP_KEEPINTVL 默认值, TCP_KEEPCNT 设为 10 probes
  • 注意区分 upstream 下的 keepalive N 这里不是指超时, N 指的是每个 Worker 与 upstream 服务器可缓存的最大连接数,

KeepAlive 使用场景

常见的几种使用场景:

  • 检测挂掉的连接(导致连接挂掉的原因很多,如服务停止、网络波动、宕机、应用重启等)
  • 防止因为网络不活动而断连(使用 NAT 代理或者防火墙的时候,经常会出现这种问题)
  • TCP 层面的心跳检测

通常很多应用程序也有类似 KeepAlive 的心跳检测, 和 TCP KeepAlive 区别在于:

  • TCP 的 KeepAlive 发送的数据包相比应用层心跳检测包更小,仅提供检测连接功能
  • 应用层心跳包不依赖于传输层协议,无论传输层协议是 TCP 还是 UDP 都可以用
  • 应用层心跳包可以定制,可以应对更复杂的情况或传输一些额外信息
  • KeepAlive 仅代表 TCP 层连接仍保持着,而心跳包往往还代表客户端可正常工作

比较 Http 协议头中 Keep-Alive

在 Http Response 的 http 头可以看到下面的字段:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 40026
Connection: keep-alive

HTTP 协议采用“请求-应答”模式,当使用普通模式,即非 KeepAlive 模式时,每个请求/应答客户和服务器都要新建一个连接,完成之后立即断开连接(HTTP 协议为无连接的协议);当使用 Keep-Alive 模式(又称持久连接、连接重用)时,Keep-Alive 功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive 功能避免了建立或者重新建立连接。

http 1.0 中默认是关闭的, http 1.1 中默认启用 Keep-Alive

@ref: https://blog.biezhi.me/2017/08/talk-tcp-keepalive.html