网络服务器程序要同时管理大量的连接,所以需要保证无用的连接完全断开,否则大量的死连接会浪费大量的服务器资源。在许多TCP国家中,有两个最值得注意:CLOSE_WAIT和TIME_WAIT.
如果服务器出现异常,80%或90%是以下两种情况:
服务器维护大量的TIME_WAIT状态;服务器维护大量的CLOSE_WAIT状态;因为linux分配给一个用户的文件句柄是有限的,如果一直保持TIME_WAIT和CLOSE_WAIT两种状态,就意味着相应数量的通道一直被占用,它们& quot占着厕所不努力& quot。一旦达到句子句柄的最大数量,就无法处理新的请求,随之而来的是大量的TooManyOpenFiles异常,tomcat就会崩溃。
在服务器的日常维护中,经常会用到以下命令:
netstat-n|awk'/^tcp/{ s[$ nf]} end { for(ains)printa,S[a]} '
例如,它显示以下信息:
时间_等待814
关闭_等待1
FIN_WAIT11
编制634
SYN_RECV2
LAST_ACK1
常用的三种状态是:ESTABLISHED表示通信,TIME_WAIT表示主动关机,CLOSE_WAIT表示被动关机。
1.收听状态
FTP服务启动后,首先处于侦听状态。
2.既定地位
建立是指建立连接。表示两台机器正在通信。
3、关闭_等待
当对方主动关闭连接或者网络异常导致连接中断时,我们的状态会变为CLOSE_WAIT,我们会调用CLOSE()来正确关闭连接。
4、时间_等待
我们主动调用close()断开连接,收到对方确认后状态变为TIME_WAIT。TCP协议规定TIME_WAIT状态将持续2MSL(即分段最大生存期的两倍),以保证旧的连接状态不会影响新的连接。TIME_WAIT状态下连接所占用的资源不会被内核释放,所以作为服务器,如果可能的话尽量不要主动断开,以减少TIME_WAIT状态造成的资源浪费。
目前有一个避免浪费TIME_WAIT资源的方法,就是关闭socket的LINGER选项。但是,TCP协议不推荐这种做法,在某些情况下,这种操作可能会带来错误。
5.同步状态
SYN_SENT状态表示连接请求。当你想访问其他电脑的服务时,你必须先向端口发送一个同步信号。此时状态为SYN_SENT,如果连接成功,将变为建立。此时,SYN_SENT状态非常短。但是,如果你发现SYN_SENT非常丰富,并且正在发送到不同的机器上,那么你的机器可能感染了冲击波或冲击波之类的病毒。这种病毒为了感染其他电脑,要对其他电脑进行扫描,并且在扫描的过程中,向每一台被扫描的电脑发出同步请求,这也是很多SYN_SENT出现的原因。
1.客户端进程发送连接释放消息并停止发送数据。释放数据报文的头,FIN=1,其序列号为seq=u(等于之前已经发送的数据最后一个字节的序列号加1)。此时,客户端进入FIN-WAIT-1状态。TCP规定,即使FIN段不携带数据,它也会消耗一个序列号。
2.服务器收到连接释放消息,发出ACK=1,ack=u 1的确认消息,自带序列号SEQ=V,此时服务器进入关闭等待状态。当TCP服务器通知更高的应用程序进程时,从客户端到服务器的方向被释放。此时处于半封闭状态,即客户端没有数据发送,但如果服务器发送数据,客户端还是要接受。这种状态将持续一段时间,即整个关闭等待状态的持续时间。
3.客户端收到服务器的确认请求后,此时客户端进入FIN-WAIT-2状态,等待服务器发送连接释放消息(在此之前,需要接受服务器发送的最后一个数据)。
4.服务器发送完最后一个数据后,向客户端发送连接释放消息,FIN=1,ack=u 1。因为是半封闭状态,服务器很可能又发了一些数据,假设此时的序列号是seq=w,此时服务器进入LAST-ACK状态,等待客户端的。
确认。5.客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2*MSL(最长报文段寿命,maxsegmentlifetime)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
6.服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
这种情况比较常见,一些爬虫服务器或者WEB服务器(如果网管在安装的时候没有做内核参数优化的话)上经常会遇到这个问题,这个问题是怎么产生的呢?
从上面的示意图可以看得出来,TIME_WAIT是主动关闭连接的一方保持的状态,对于爬虫服务器来说它本身就是客户端,在完成一个爬取任务之后,它就会发起主动关闭连接,从而进入TIME_WAIT的状态。然后在保持这个状态2MSL(maxsegmentlifetime)时间之后,彻底关闭回收资源。
为什么要这么做?明明就已经主动关闭连接了为啥还要保持资源一段时间呢?
这个是TCP/IP的设计者规定的,主要出于以下两个方面的考虑:
1.防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)。
2.可靠的关闭TCP连接。在主动关闭方发送的最后一个ack(fin),有可能丢失,这时被动方会重新发fin,如果这时主动方处于CLOSED状态,就会响应rst而不是ack。所以主动方要处于TIME_WAIT状态,而不能是CLOSED。另外这么设计TIME_WAIT会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。
TIME_WAIT状态可以通过优化服务器参数得到解决,因为发生TIME_WAIT的情况是服务器自己可控的,要么就是对方连接的异常,要么就是自己没有迅速回收资源,总之不是由于自己程序错误导致的。
但是CLOSE_WAIT就不一样了,从上面的图可以看出来,如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方关闭连接之后服务器程序自己没有进一步发出ack信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直被程序占着。
执行如下命令:
netstat-n|awk'/^tcp/{++S[$NF]}END{for(ainS)printa,S[a]}'
发现CLOSE_WAIT的数量始终在400以上,一直没降过。
个人觉得这种情况,通过服务器内核参数也没办法解决,服务器对于程序抢占的资源没有主动回收的权利,除非终止程序运行。
如果你使用的是HttpClient并且你遇到了大量CLOSE_WAIT的情况,那么这篇日志也许对你有用:
HttpClient连接池抛出大量ConnectionPoolTimeoutException:Timeoutwaitingforconnection异常排查
http://blog.csdn.net/shootyou/article/details/6615051
在那边日志里头我举了个场景,来说明CLOSE_WAIT和TIME_WAIT的区别,这里重新描述一下:
服务器A是一台爬虫服务器,它使用简单的HttpClient去请求资源服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完资源后,服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,服务器A的连接状态我们可以看到是TIME_WAIT。
如果一旦发生异常呢?假设请求的资源服务器B上并不存在,那么这个时候就会由服务器B发出关闭连接的请求,服务器A就是被动的关闭了连接,如果服务器A被动关闭连接之后程序员忘了让HttpClient释放连接,那就会造成CLOSE_WAIT的状态了。
所以如果将大量CLOSE_WAIT的解决办法总结为一句话那就是:查代码。因为问题出在服务器程序里头。
上一篇:芥末鱿鱼的做法窍门
下一篇:行驶证年检盖章满了怎么办