长连接是指网络传输层使用 TCP 协议经过三次握手建立的连接。
优势
减少连接建立过程中的耗时。数据交互(push模式)实现的前提是网络长连接,有了长连接,连接两端很方便的互相 push 数据以进行交互。
心跳
检测一个系统是否存活或者网络链路是否通畅的一种方式,其一般做法是定时向被检测系统发送心跳包,被检测系统收到心跳包进行回复,收到回复说明对方存活。心跳能够给长连接提供保活功能,能够检测长连接是否正常。
疑问:为什么需要应用层心跳机制?
TCP 连接到底是什么?
为实现数据可靠传输,由通信双方通过三次握手交互而建立的逻辑连接,通信双方都需要维护这样的连接状态信息。比如 netstat 中连接状态为 ESTABLISHED,表示当前处于连接状态(这里需要注意的是这个ESTABLISHED 的连接状态只是操作系统认为当前还处在连接状态)
是不是建立了长连接,就可以高枕无忧了呢?
建立了长连接,两端的操作系统都维护了连接已经建立的状态,是不是这时向对端发送数据一定能到达呢?答案是否定的。
可能此时链路已经不通,只是 TCP 层还没有感知到这一信息,操作系统层面显示的状态依然是连接状态,因为 TCP 层还认为连接是 ESTABLISHED,所以作为应用层自然也就无法感知当前的链路不通。
这种情况会导致什么问题?
如果此时有数据想要传输,显然,数据是无法传送到对端,但是 TCP 协议为了保证可靠性,会重传请求,如果问题只是网线接头松了,导致网络不通,此时如果及时将网线接头接好,数据还是能正常到达对端,且 TCP 的连接依然是 ESTABLISHED,不会有任何变化。但不是任何时候都这么巧,有时就是某段链路瘫痪了,或者主机挂了,系统异常关闭了等。这时候如果应用系统不能感知到,是件很危险的事情。
长连接怎么保活?
TCP 协议中是有保活机制的,即 TCP 的 KeepAlive 机制(此机制并不是 TCP 协议规范中的内容,由操作系统去实现),KeepAlive 机制开启后,在一定时间内(一般时间为7200s,参数tcp_keepalive_time)在链路上没有数据传送的情况下,TCP 层将发送相应的 KeepAlive 探针以确定连接可用性,探测失败后重试 10(参数tcp_keepalive_probes)次,每次间隔时间 75s(参数tcp_keepalive_intvl),所有探测失败后,才认为当前连接已经不可用。这些参数是机器级别,可以调整。
应用层需要做点什么吗?
按照 TCP 的 KeepAlive 机制,默认的参数,显然不能满足要求。那是不是调小点就可以了呢? 调整参数,当然是有用的,但是首先参数的机器级别的,调整起来不太方便,更换机器还得记得调整参数,对系统的使用方来说,未免增加了维护成本,而且很可能忘记;
其次由于 KeepAlive 的保活机制只在链路空闲的情况下才会起到作用,假如此时有数据发送,且物理链路已经不通,操作系统这边的链路状态还是ESTABLISHED,这时会发生什么?自然会走 TCP 重传机制,要知道默认的 TCP超时重传,指数退避算法也是一个相当长的过程。因此,一个可靠的系统,长连接的保活肯定是要依赖应用层的心跳来保证的。
这里应用层的心跳举个例子,比如客户端每隔 3s 通过长连接通道发送一个心跳请求到服务端,连续失败 5 次就断开连接。这样算下来最长 15s 就能发现连接已经不可用,一旦连接不可用,可以重连,也可以做其他的failover 处理,比如请求其他服务器。
应用层心跳还有个好处,比如某台服务器因为某些原因导致负载超高,CPU飙高,或者线程池打满等等,无法响应任何业务请求,如果使用 TCP 自身的机制无法发现任何问题,然而对客户端而言,这时的最好选择就是断连后重新连接其他服务器,而不是一直认为当前服务器是可用状态,向当前服务器发送一些必然会失败的请求。
HTTP 1.1 规定默认保持长连接(HTTP persistent connection),数据传输完成时保持 TCP 连接不断开(不发 RST 包、不四次握手),等待在同域名下(ip:port)继续用这个通道传输数据。相反的就是短连接。 HTTP的 Keep-alive 是要让一个 TCP 连接为持久连接
参考文档
https://www.cnblogs.com/cswuyg/p/3653263.html https://www.jianshu.com/p/c6af08f853d0 https://www.cnblogs.com/0201zcr/p/4694945.html