这阵子突然重新接手做了下关于TCP连接异常中断的检测, 这里的异常中断指的是目标主机断电, 网线被拔等非正常因素.
这个问题已经被处理很多年了, 无非2种基本解决策略:
1. 应用层心跳
2. 开启协议栈提供的keepalive, 这个和操作系统有关 有些操作系统提供的协议栈并不支持该特性, 因为TCP/IP协议标准中并没有要求实现该特性.
刚刚接手的时候, 直接做了一个应用层心跳, 不过后来和同事讨论了下, 发现要求尽可能少的添加代码, 故转向使用keepalive
不过, keepalive有个特点, 只有连接"空闲"时才会激活保活定时器, 这点就比较纠结了.. 这里的空闲是指该连接的接收和发送buff中均为空才认定为空闲. 所以无论对于服务器还是客户端都是一直在不断的收发数据的, 连接突然中断时, socket buff中存在数据是概率很大的, 一旦存在数据的话, 协议栈就会采用指数退避原则尝试重传导致连接始终为非空闲, 自然keepalive也就无效. 所以为了避免这个问题, 还需要开辟一条特殊占用的空闲TCP连接, 这条连接不传输任何数据, 仅仅作为保活检查.
事情到此似乎解决完了, 不过对于我个人, 性格上不允许我就这样停止... 查阅大量资料, 又通过tcpdump检测异常断开的情况, 发现当连接断开时协议栈是会收到icmp报文, 告知主机不可达, 不过这个只是条类似软中断的提示, 协议栈忽略了该icmp的指示, 认为可能对方网络只是短暂的故障, 并不断开, 所以就发生了采用指数退避策略进行重传的情况. 对于这点, 我倒是希望可以通过一定的技术手段从获得icmp(主机不可达)报文的时候开始计数, 即控制重传次数, 缩短故障检测时间.
另外对于引入一个专门的监控进程, 客户端直接和监控进程通信, 这样使逻辑从服务器端分离出来, 还可以做更多的工作, 不过这个是否必要感觉还是需要根据项目具体的分析才能得出结论
ps: 这个问题我又问了下陈硕, 补充下keepalive方面的问题, keepalive确实可以做到检测tcp连接上的问题, 但是如果进程内部死锁了, 其实质上该进程已经不能提供服务了, 这时候还是应该需要检测出来尽快的切换到下一个备用服务器上, 这方面还是应该依赖应用层心跳机制 再次感谢陈硕(其blog地址:http://blog.csdn.net/Solstice)~