谷动谷力

标题: TCP 中的 KeepAlive 断线重连机制 [打印本页]

作者: 谷谷小师妹    时间: 2023-11-22 23:39
标题: TCP 中的 KeepAlive 断线重连机制



TCP 中的 KeepAlive 断线重连机制

服务端的系统设置中经常会和底层协议打交道,我们有必要重温一下曾经那些“听过”却不熟悉的名词。
今天聊的话题是 KeepAlive,在实际应用中又是怎么使用的?
为什么有Keepalive?
大家都做过电梯吧,假设电梯来了你先进去,你朋友还没进来,过一段时间电梯门就会自动关闭,你应该没遇到过哪个电梯会一直等你朋友来了才关门的。如果真是那样,那别的楼层的小姐姐们会炸了~
我们举个编程中的例子来解释下,我编写了一个服务端程序S和一个客户端程序C,客户端向服务端发送一个消息:
客户端发送消息
服务端收到消息后一看,瞧给你牛*的,然后没理客户端,傻狗客户端一直在等待,但是不知道是不是服务器挂掉了?

这时候TCP协议提出一个办法,当客户端端等待超过一定时间后自动给服务端发送一个空的报文,如果对方回复了这个报文证明连接还存活着,如果对方没有报文返回且进行了多次尝试都是一样,那么就认为连接已经丢失,客户端就没必要继续保持连接了。如果没有这种机制就会有很多空闲的连接占用着系统资源。
KeepAlive并不是TCP协议规范的一部分,但在几乎所有的TCP/IP协议栈(不管是Linux还是Windows)中,都实现了KeepAlive功能。
RFC1122#TCP Keep-Alives
如何设置它?
在设置之前我们先来看看KeepAlive都支持哪些设置项
我们讲讲在Linux操作系统和使用Java、C语言以及在Nginx如何设置
在Linux内核设置
KeepAlive默认不是开启的,如果想使用KeepAlive,需要在你的应用中设置SO_KEEPALIVE才可以生效。
查看当前的配置:
  1. cat /proc/sys/net/ipv4/tcp_keepalive_time
  2. cat /proc/sys/net/ipv4/tcp_keepalive_intvl
  3. cat /proc/sys/net/ipv4/tcp_keepalive_probes
复制代码


在Linux中我们可以通过修改 /etc/sysctl.conf 的全局配置:
  1. net.ipv4.tcp_keepalive_time=7200
  2. net.ipv4.tcp_keepalive_intvl=75
  3. net.ipv4.tcp_keepalive_probes=9
复制代码


添加上面的配置后输入 sysctl -p 使其生效,你可以使用 sysctl -a | grep keepalive 命令来查看当前的默认配置
如果应用中已经设置SO_KEEPALIVE,程序不用重启,内核直接生效
使用Netty4设置
这里我们使用常用的Java网络框架Netty来设置,只需要在服务端设置即可:
  1. EventLoopGroup bossGroup   = new NioEventLoopGroup(1);
  2. EventLoopGroup workerGroup = new NioEventLoopGroup();
  3. try {
  4.    ServerBootstrap b = new ServerBootstrap();
  5.    b.group(bossGroup, workerGroup)
  6.            .channel(NioServerSocketChannel.class)
  7.            .option(ChannelOption.SO_BACKLOG, 100)
  8.            .childOption(ChannelOption.SO_KEEPALIVE, true)
  9.            .handler(new LoggingHandler(LogLevel.INFO));

  10.    // Start the server.
  11.    ChannelFuture f = b.bind(8088).sync();
  12.    // Wait until the server socket is closed.
  13.    f.channel().closeFuture().sync();
  14. } finally {
  15.    // Shut down all event loops to terminate all threads.
  16.    bossGroup.shutdownGracefully();
  17.    workerGroup.shutdownGracefully();
  18. }
复制代码


这段代码来自经典的echo服务器,我们在childOption中开启了SO_KEEPALIVE。
Java程序只能做到设置SO_KEEPALIVE选项,其他配置项只能依赖于sysctl配置,系统进行读取。
C语言设置
函数原型:
  1. #include <sys/socket.h>

  2. int setsockopt(int socket, int level, int option_name,
  3.      const void *option_value, socklen_t option_len);
复制代码


我们在需要使能Keepalive的socket上面调用setsockopt函数便可以打开该socket上面的keepalive。
调用例子:
  1. int socket(int domain, int type, int protocol)
  2. {
  3. int (*libc_socket)(int, int, int);
  4. int s, optval;
  5. char *env;

  6. *(void **)(&libc_socket) = dlsym(RTLD_NEXT, "socket");
  7. if(dlerror()) {
  8.    errno = EACCES;
  9.    return -1;
  10. }

  11. if((s = (*libc_socket)(domain, type, protocol)) != -1) {
  12.    if((domain == PF_INET) && (type == SOCK_STREAM)) {
  13.      if(!(env = getenv("KEEPALIVE")) || strcasecmp(env, "off")) {
  14.        optval = 1;
  15.      } else {
  16.        optval = 0;
  17.      }
  18.      if(!(env = getenv("KEEPALIVE")) || strcasecmp(env, "skip")) {
  19.        setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
  20.      }
  21. #ifdef TCP_KEEPCNT
  22.      if((env = getenv("KEEPCNT")) && ((optval = atoi(env)) >= 0)) {
  23.        setsockopt(s, SOL_TCP, TCP_KEEPCNT, &optval, sizeof(optval));
  24.      }
  25. #endif
  26. #ifdef TCP_KEEPIDLE
  27.      if((env = getenv("KEEPIDLE")) && ((optval = atoi(env)) >= 0)) {
  28.        setsockopt(s, SOL_TCP, TCP_KEEPIDLE, &optval, sizeof(optval));
  29.      }
  30. #endif
  31. #ifdef TCP_KEEPINTVL
  32.      if((env = getenv("KEEPINTVL")) && ((optval = atoi(env)) >= 0)) {
  33.        setsockopt(s, SOL_TCP, TCP_KEEPINTVL, &optval, sizeof(optval));
  34.      }
  35. #endif
  36.    }
  37. }

  38.   return s;
  39. }
复制代码


代码摘取自libkeepalive源码,C语言可以设置更为详细的TCP内核参数
在Nginx中配置
在Nginx中配置TCP的KeepAlive非常简单,在listen指令下配置so_keepalive就可以了,具体配置
  1. 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连接
建立一个保护记录,相对于应用层面效率更高。
常见的几种使用场景:
KeepAlive通过定时发送探测包来探测连接的对端是否存活,
但通常也会许多在业务层面处理的,他们之间的特点:
和Http中Keep-Alive的关系参考资料



编辑于 2018-09-01 23:50






欢迎光临 谷动谷力 (http://bbs.sunsili.com/) Powered by Discuz! X3.2