聊聊 TCP 中的 KeepAlive 机制

标签: 基础技术 KeepAlive TCP | 发表时间:2017-12-19 00:01 | 作者:TiuVe2
出处:http://www.importnew.com

服务端的系统设置中经常会和底层协议打交道,我们有必要重温一下曾经那些“听过”却不熟悉的名词。 今天聊的话题是 KeepAlive,在实际应用中又是怎么使用的?

为什么有Keepalive?

大家都做过电梯吧,假设电梯来了你先进去,你朋友还没进来,过一段时间电梯门就会自动关闭, 你应该没遇到过哪个电梯会一直等你朋友来了才关门的。如果真是那样,那别的楼层的小姐姐们会炸了~

我们举个编程中的例子来解释下,我编写了一个服务端程序S和一个客户端程序C,客户端向服务端发送 一个消息:

服务端收到消息后一看,瞧给你牛*的,然后没理客户端,傻狗客户端一直在等待,但是不知道是不是服务器挂掉了? 这时候TCP协议提出一个办法,当客户端端等待超过一定时间后自动给服务端发送一个空的报文, 如果对方回复了这个报文证明连接还存活着,如果对方没有报文返回且进行了多次尝试都是一样, 那么就认为连接已经丢失,客户端就没必要继续保持连接了。 如果没有这种机制就会有很多空闲的连接占用着系统资源。

KeepAlive并不是TCP协议规范的一部分,但在几乎所有的TCP/IP协议栈(不管是Linux还是Windows)中,都实现了KeepAlive功能

RFC1122#TCP Keep-Alives

如何设置它?

在设置之前我们先来看看KeepAlive都支持哪些设置项

  1. KeepAlive默认情况下是关闭的,可以被上层应用开启和关闭
  2. tcp_keepalive_time: KeepAlive的空闲时长,或者说每次正常发送心跳的周期,默认值为7200s(2小时)
  3. tcp_keepalive_intvl: KeepAlive探测包的发送间隔,默认值为75s
  4. tcp_keepalive_probes: 在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包次数,默认值为9(次)

我们讲讲在Linux操作系统和使用Java、C语言和Nginx中如何设置

在Linux内核设置

KeepAlive默认不是开启的,如果想使用KeepAlive,需要在你的应用中设置SO_KEEPALIVE才可以生效。

查看当前的配置:

cat /proc/sys/net/ipv4/tcp_keepalive_time
cat /proc/sys/net/ipv4/tcp_keepalive_intvl
cat /proc/sys/net/ipv4/tcp_keepalive_probes

在Linux中我们可以通过修改 /etc/sysctl.conf 的全局配置:

net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
net.ipv4.tcp_keepalive_probes=9

添加上面的配置后输入 sysctl -p 使其生效,你可以使用 sysctl -a | grep keepalive 命令来查看当前的默认配置

如果应用中已经设置SO_KEEPALIVE,程序不用重启,内核直接生效

使用Netty4设置

这里我们使用常用的Java网络框架Netty来设置,只需要在服务端设置即可:

EventLoopGroup bossGroup   = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
    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));

    // Start the server.
    ChannelFuture f = b.bind(8088).sync();
    // Wait until the server socket is closed.
    f.channel().closeFuture().sync();
} finally {
    // Shut down all event loops to terminate all threads.
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
}

这段代码来自经典的echo服务器,我们在childOption中开启了SO_KEEPALIVE。 Java程序只能做到设置SO_KEEPALIVE选项,其他配置项只能依赖于sysctl配置,系统进行读取。

C语言设置

函数原型:

#include <sys/socket.h>

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

我们在需要使能Keepalive的socket上面调用setsockopt函数便可以打开该socket上面的keepalive。

  1. 第一个参数是要设置的套接字
  2. 第二个参数是SOL_SOCKET
  3. 第三个参数必须是SO_KEEPALIVE
  4. 第四个参数必须是一个布尔整型值,0表示关闭,1表示打开
  5. 最后一个参数是第四个参数值的大小。

调用例子:

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));
      }
#ifdef TCP_KEEPCNT
      if((env = getenv("KEEPCNT")) && ((optval = atoi(env)) >= 0)) {
        setsockopt(s, SOL_TCP, TCP_KEEPCNT, &optval, sizeof(optval));
      }
#endif
#ifdef TCP_KEEPIDLE
      if((env = getenv("KEEPIDLE")) && ((optval = atoi(env)) >= 0)) {
        setsockopt(s, SOL_TCP, TCP_KEEPIDLE, &optval, sizeof(optval));
      }
#endif
#ifdef TCP_KEEPINTVL
      if((env = getenv("KEEPINTVL")) && ((optval = atoi(env)) >= 0)) {
        setsockopt(s, SOL_TCP, TCP_KEEPINTVL, &optval, sizeof(optval));
      }
#endif
    }
  }

   return s;
}

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

在Nginx中配置

在Nginx中配置TCP的KeepAlive非常简单,在listen指令下配置so_keepalive就可以了,具体配置

so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]

this parameter (1.1.11) configures the “TCP keepalive” behavior for the listening socket. If this parameter is omitted then the operating system’s settings will be in effect for the socket. If it is set to the value “on”, the SO_KEEPALIVE option is turned on for the socket. If it is set to the value “off”, the SO_KEEPALIVE option is turned off for the socket. Some operating systems support setting of TCP keepalive parameters on a per-socket basis using the TCP_KEEPIDLE, TCP_KEEPINTVL, and TCP_KEEPCNT socket options. On such systems (currently, Linux 2.4+, NetBSD 5+, and FreeBSD 9.0-STABLE), they can be configured using the keepidle, keepintvl, and keepcnt parameters. One or two parameters may be omitted, in which case the system default setting for the corresponding socket option will be in effect.

例子

so_keepalive=30m::10
will set the idle timeout (TCP_KEEPIDLE) to 30 minutes,
leave the probe interval (TCP_KEEPINTVL) at its system default,
and set the probes count (TCP_KEEPCNT) to 10 probes.

使用的场景

一般我们使用KeepAlive时会修改空闲时长,避免资源浪费,系统内核会为每一个TCP连接 建立一个保护记录,相对于应用层面效率更高。

常见的几种使用场景:

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

KeepAlive通过定时发送探测包来探测连接的对端是否存活, 但通常也会许多在业务层面处理的,他们之间的特点:

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

和Http中Keep-Alive的关系

  1. HTTP协议的Keep-Alive意图在于连接复用,同一个连接上串行方式传递请求-响应数据
  2. TCP的KeepAlive机制意图在于保活、心跳,检测连接错误

参考资料

相关文章

相关 [tcp keepalive] 推荐:

聊聊 TCP 中的 KeepAlive 机制

- - ImportNew
服务端的系统设置中经常会和底层协议打交道,我们有必要重温一下曾经那些“听过”却不熟悉的名词. 今天聊的话题是 KeepAlive,在实际应用中又是怎么使用的. 为什么有Keepalive. 大家都做过电梯吧,假设电梯来了你先进去,你朋友还没进来,过一段时间电梯门就会自动关闭, 你应该没遇到过哪个电梯会一直等你朋友来了才关门的.

KeepAlive详解 - HavenShen

- - 博客园_首页
KeepAlive既熟悉又陌生,踩过坑的同学都知道痛. 一线运维工程师踩坑之后对于KeepAlive的总结,你不应该错过. 最近工作中遇到一个问题,想把它记录下来,场景是这样的:. 从上图可以看出,用户通过Client访问的是LVS的VIP, VIP后端挂载的RealServer是Nginx服务器.

nginx + keepalive 实现HA

- - CSDN博客编程语言推荐文章
主nginx负载均衡器 192.168.166.203. 辅nginx负载均衡器 192.168.166.177. VIP地址 192.168.166.178. 二.修改配置文件为以下内容: [master slave].  state MASTER #(主机为MASTER,备用机为BACKUP).

HTTP的KeepAlive是开启还是关闭?

- - CSDN博客系统运维推荐文章
1、KeepAlive的概念与优势.         HTTP的KeepAlive就是浏览器和服务端之间保持长连接,这个连接是可以复用的. 当客户端发送一次请求,收到相应内容后,这个连接会保持一段时间,在该时间内的第二次就不需要再重新建立连接,就可以直接使用这次的连接来发送请求了,极大的提高了速度.

tcp/ip调优

- Lucseeker - 在路上
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认;. 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;.

使用 Nginx 的 keepalive patch,nginx+memcached的TPS提升7倍

- 2sin18 - Linux@SOHU
编者按:本月初 Maxim Dounin,Nginx 最活跃的开发者之一,提交了 upstream keepalive patch,支持 http/fastcgi/memcached,除了减少和 upstream 的网络开销外,也意味着能反向代理 http chunked 响应了. 搜狐技术部CMS组的同学进行了一个简单的测试:.

浅谈TCP优化

- - 火丁笔记
很多人常常对 TCP优化有一种雾里看花的感觉,实际上只要理解了TCP的运行方式就能掀开它的神秘面纱. Ilya Grigorik 在「 High Performance Browser Networking」中做了很多细致的描述,让人读起来醍醐灌顶,我大概总结了一下,以期更加通俗易懂. 传输数据的时候,如果发送方传输的数据量超过了接收方的处理能力,那么接收方会出现丢包.

TCP报文结构

- - 互联网 - ITeye博客
一、TCP报文结构如下:.  固定首部长度为20字节,可变部分0~40字节,各字段解释:. source port number:源端口,16bits,范围0~65525. target port number:目的端口,16bits,范围同上. sequence number:数据序号,32bits,TCP 连接中传送的数据流中的每一个字节都编上一个序号.

TCP 状态变化

- - 互联网 - ITeye博客
关闭socket分为主动关闭(Active closure)和被动关闭(Passive closure)两种情况. 前者是指有本地主机主动发起的关闭;而后者则是指本地主机检测到远程主机发起关闭之后,作出回应,从而关闭整个连接. 将关闭部分的状态转移摘出来,就得到了下图:. 通过图上,我们来分析,什么情况下,连接处于CLOSE_WAIT状态呢.

TCP/IP分享——链路层

- Goingmm - 弯曲评论
在张国荣自尽8周年纪念日,也就是愚人节的前几十分钟,终于把第二章弄完了. 首席似乎不是特别有空,我就斗胆在这里自己发了,从前面2期的反响来看,相当热烈,我也是摆出一副要杀要剐,悉听尊便的架势,这可能是受最近流行霸气外露的影响,批评几句又伤不了皮毛,也影响不了我的工作和正常生活,只要给大家带来快乐,我就很开心,似乎历史上很多想法都是在争吵中诞生的.