CLOSE_WAIT问题分析
问题描述:
在做websocket压测的时候,在一台4核8G服务器上,创建2w长连接,压测完成后,客户端同时断开2w连接,瞬时服务端服务器CPU被打满,服务端存在大量CLOSE_WAIT状态连接,因连接不释放和CPU持续打满,导致服务不可用。
TCP三次握手:
第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;(x 是随机生成的一个 int 数值)然后,客户端进入SYN_SEND状态,等待服务器的确认;
第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为 y (y 是随机生存的一个 int 数值);服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
TCP四次挥手:
第一次挥手:Client (可以使客户端,也可以是服务器端),设置Sequence Number和Acknowledgment Number,向 Server发送一个FIN报文段;此时,Client 进入FIN_WAIT_1状态;这表示 Client 没有数据要发送给 Server了。(客户端发送第一次挥手后,就不能在向 服务端发送数据了)
第二次挥手:Server 收到了 Client 发送的FIN报文段,向 Client 回一个ACK报文段,Acknowledgment Number 为 Sequence Number 加 1;Client 进入 FIN_WAIT_2 状态;Server 告诉 Client ,我“同意”你的关闭请求(Server 第一次响应后,还可以继续向 Client 发送数据,这里只是告诉 Client ,我收到你发送的关闭请求)
第三次挥手:Server 向 Client 发送 FIN 报文段,请求关闭连接,同时 Server 进入 CLOSE_WAIT 状态;(当 Server 的数据响应完成后,再告诉 Client,我这边也可以关闭请求了, 这时Server 就不能再向 Client 发送数据了)
第四次挥手:Client 收到 Server 发送的 FIN 报文段,向 Server 发送 ACK 报文段,然后 Client 进入TIME_WAIT 状态;Server 收到 Client 的 ACK 报文段以后,就关闭连接;此时,Client等待2MSL后依然没有收到回复,则证明 Server 端已正常关闭,那好,Client 也可以关闭连接了。
通常,CLOSE_WAIT 状态在服务器停留时间很短,如果你发现大量的 CLOSE_WAIT 状态,那么就意味着被动关闭的一方没有及时发出 FIN 包,一般有如下几种可能:程序问题:如果代码层面忘记了 close 相应的 socket 连接,那么自然不会发出 FIN 包,从而导致 CLOSE_WAIT 累积;或者代码不严谨,出现死循环之类的问题,导致即便后面写了 close 也永远执行不到。响应太慢或者超时设置过小:如果连接双方不和谐,一方不耐烦直接 timeout,另一方却还在忙于耗时逻辑,就会导致 close 被延后。响应太慢是首要问题,不过换个角度看,也可能是 timeout 设置过小。BACKLOG 太大:此处的 backlog 不是 syn backlog,而是 accept 的 backlog,如果 backlog 太大的话,设想突然遭遇大访问量的话,即便响应速度不慢,也可能出现来不及消费的情况,导致多余的请求还在队列里就被对方关闭了。