场景:如果在一个消息系统中,服务器崩溃,但是客户端并没有收到连接中断FIN包(由于异常情况,该FIN包没能正常到达客户端),这样导致的结果就是,客户端一直维护着一个"过时的"连接,不再会收到服务器发来的消息。 这个故障的根本原因在于:客户端没有及时对连接的有效性进行检测。
TCP Keep-Alive 选项
Keep-Alive是TCP的一个用来保持活跃的机制。
机制原理:
定义一个时间段(变量1),在这个时间段内,如果没有任何连接相关的活动,TCP保持活跃机制就会开始作用,每隔一个时间间隔(变量2),发送一个探测报文,该探测报文所含的数据非常少,如果连续几个探测报文(变量3)都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。(即首先要经过保持活跃时间这个时间段(2小时),如果这个时间段内没有连接相关活动,那么开启保持活跃机制,即开始发送探测报文)
其中的三个变量分别是保持活跃时间、保持活跃时间间隔和保持活跃探测次数,在 Linux 系统中,这些变量分别对应 sysctl 变量 net.ipv4.tcp_keepalive_time、net.ipv4.tcp_keepalive_intvl、 net.ipv4.tcp_keepalve_probes ,默认设置是 7200 秒(2 小时)、75 秒和 9 次探测。
在开启了TCP保持活跃机制后,有以下几种情况:
- 对端程序正常工作。当 TCP 保活的探测报文发送给对端, 对端会正常响应,这样 TCP 保活时间会被重置(2小时) ,等待下一个 TCP 探活时间的到来。
- 对端程序崩溃并重启。当 TCP 保活的探测报文发送给对端后,对端是可以响应的,但由于没有该连接的有效信息,会产生一个 RST 报文,这样很快就会发现 TCP 连接已经被重置。
- 对端程序崩溃,或对端由于其他原因导致报文不可达。当 TCP 保活的探测报文发送给对端后,石沉大海,没有响应,连续几次,达到保活探测次数后,TCP 会报告该 TCP 连接已经死亡。
TCP保持活跃机制默认关闭,可以在连接的两个方向上开启,也可以在单独的一个方向开启。
如果开启服务器端到客户端的检测,就可以在客户端非正常断连的情况下清除在服务器端保留的“脏数据” ;
如果开启客户端到服务器端的检测,就可以在服务器无响应的情况下,重新发起连接。
在使用Keep-Alive机制时,在Linux系统中,如果想要发现一个"死亡"的连接,最少需要经过 2 小时 11 分 15 秒,即2 小时,加上 75 秒乘以 9 的总和,在对很多对时延要求敏感的系统中,这个时间间隔是不可接受的。(先经过保持活跃时间2小时,没有连接活动的话,开始进行探测,即还要经过75秒 * 9次)
综上,由于发现一个"死亡"的连接所需要的时间太长,所以还需要在应用程序这一层,探寻更好的解决方案。
应用层连接探活
通过在应用程序中模拟TCP Keep-Alive 机制,来完成在应用层的连接探活。
具体做法:
在应用层设计一个 PING-PONG 机制,需要保持活跃的一方,比如客户端,在保活时间达到后,发起对连接的PING操作,如果服务器端对PING操作有回应,则重新设置保活时间,否则就需要对保活探测次数进行计数,如果最终探测次数达到了保活探测次数预先设置的值后,则认为连接已经无效。
关键点:1. 使用定时器 2. 设计一个 PING-PONG 协议