Fuhui

长连接


遗留问题

tcp中的keep-alive

作用:保活、心跳、链路有效检测机制、定时清理失效的连接;当一个TCP连接两端长时间没有数据传输时(通常默认配置是2小时),发送keepalive探针,探测链接是否存活

原理:tcp的keep-alive机制发送的探测报文是将之前TCP报文的序列号减1,并设置1个字节,内容为“00”的应用层数据,这样对端能识别这个报文是tcp keep-alive探测报文并进行ack。 建立TCP连接时,就有定时器与之绑定,其中的一些定时器就用于处理keepalive过程。当keepalive定时器到0的时候,便会给对端发送一个不包含数据部分的keepalive探测包(probe packet)

一方发起的keepalive探针,另一方必须响应。响应可能是以下三种形式之一:

1. 对方回应了ACK。说明连接正常,重置计时器重新记时。如果接下来2小时还没有数据传输,那么还会继续发送keepalive探针,以确保连接存活。
2. 对方回复RST(意思是要求对端关闭异常连接且对端不需要回复 ACK)。表示这个连接已经不存在,那就关闭该连接。例如一方服务宕机后重启/程序崩溃
3. 没有回复。那就会触发超时重传,直到达到重传的次数,如果对端依然没有回复,那么就关闭该连接,说明socket已经被关闭了

相关配置:

$ ls /proc/sys/net/ipv4 | grep tcp_keepalive
tcp_keepalive_intvl     keepalive探测包的发送间隔,默认为75秒
tcp_keepalive_probes    如果对方不予应答,探测包的最大发送次数,默认为9次。即连续9次发送,都没有应答的话,则关闭连接。
tcp_keepalive_time      连接的最大空闲(idle)时间,默认为7200秒,即2个小时。需要注意的是,这2个小时,指的是只有keepalive探测包,如果期间存在其他数据传输,则重新计时。

可以通过sysctl命令来查看和修改:

查看
sysctl -n  net.ipv4.tcp_keepalive_time
修改
sysctl net.ipv4.tcp_keepalive_time=3600
添加上面的配置后输入 sysctl -p 使其生效,你可以使用 sysctl -a | grep keepalive 命令来查看当前的默认配置

常见的几种使用场景:

KeepAlive心跳和业务层面的心跳报文相比:

http的keep-alive

作用:长连接,复用tcp连接,提高socket效率,减少tcp连接数,也就相应减少了主动关闭方的time_wait状态连接数,提高性能和提高httpd服务器的吞吐率 (更少的tcp连接意味着更少的系统内核调用,socket的accept()和close()调用)

缺点:长时间的tcp连接容易导致系统资源无效占用,处于长时间闲置状态,即在处理暂停期间,本来可以释放的资源仍旧被占用

限制:HTTP1.1的Keep-Alive机制仅对同域下的网络请求有效;而对于非同域下的请求,则需要重新建立一次TCP连接。 事实上,即使客户端和服务端都开启了Keep-Alive,服务端一般会主动关闭非活动的连接,否则会造成资源浪费

Keep-Alive模式下如何知道某一次数据传输结束

如果不是Keep-Alive模式,HTTP协议中客户端发送一个请求,服务器响应其请求,返回数据。服务器通常在发送回所请求的数据之后就关闭连接。这样客户端读数据时会返回EOF(-1),就知道数据已经接收完了。

但是如果开启了 Keep-Alive模式,那么客户端如何知道某一次的响应结束了呢?

HTTP keep-alive和TCP keepalive的区别

TCP keepalive指的是TCP保活计时器(keepalive timer)。设想有这样的情况:客户已主动与服务器建立了TCP连接。但后来客户端的主机突然出故障。 显然,服务器以后就不能再收到客户发来的数据。因此,应当有措施使服务器不要再白白等待下去。这就是使用保活计时器。服务器每收到一次客户的数据,就重新设置保活计时器,时间的设置通常是两小时。若两小时没有收到客户的数据,服务器就发送一个探测报文段,以后则每隔75秒发送一次。若一连发送10个探测报文段后仍无客户的响应,服务器就认为客户端出了故障,接着就关闭这个连接。

仅当nginx的keepalive_timeout值设置高于tcp_keepalive_time,并且距此tcp连接传输的最后一个http响应,经过了tcp_keepalive_time时间之后, 操作系统才会发送侦测包来决定是否要丢弃这个TCP连接。一般不会出现这种情况,除非你需要这样做。

http1.0 vs http1.1 vs http2.0

协议是这样规定的,至于支不支持还得看服务器(比如tomcat)和客户端(比如浏览器)的具体实现

http1.0/ 1.1 /2 需要客户端和服务器都支持;firefox可用通过debug的network查看http version

服务器进程通知TCP断开TCP连接(只是通知,此时TCP连接不一定关闭),TCP发送完数据后,实际断开该连接

参考

  1. http://www.tianshouzhi.com/api/tutorials/netty/406
  2. https://blog.csdn.net/xiaoduanayu/article/details/78386508
  3. https://blog.chionlab.moe/2016/09/24/linux-tcp-keepalive/
  4. https://www.zhuxiaodong.net/2018/tcp-http-keepalive/