记一次CLOSE_WAIT
背景:
预发布环境在拉取代码时,页面报Curl Failed :Connection reset by peer ,php后台报connection timeout是php代码部分调用拉取接口时报出来的,通过netstat查看该接口目前的tcp连接状态
可以看到服务端的连接一直处于close_wait状态,连接一直没释放掉,由于该接口是单线程的,导致新的请求一直阻塞(从第二列可以看到),用户发现一直响应不了就会频繁刷新网页,导致连接数剧增
第二列和第三列的含义如下:
第二列表示网络接收队列(recv-Q)
表示收到的数据已经在本地缓冲区,但是还有多少没有被对端服务取走,
如果接收队列Recv-Q一直处于阻塞状态,可能是遭受了拒绝服务 denial-of-service 攻击,说白了就是产生了很多连接导致服务端处理不过来了。
第三列表示网路发送队列(send-Q)
对方没有收到的数据或者说没有Ack的,还是本地缓冲区.
如果发送队列Send-Q不能很快的清零,可能是有应用向外发送数据包过快,或者是对方接收数据包过慢。
这个问题可以通过一下代码复现
服务端
package main
import (
"net"
"time"
)
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:8888")
if err != nil {
panic(err)
}
defer listen.Close()
for {
conn, err := listen.Accept()
if err != nil {
panic(err)
}
time.Sleep(20*time.Second)
conn.Close()
}
}
客户端
package main
import (
"log"
"net"
"time"
)
func main(){
conn, err := net.Dial("tcp", "127.0.0.1:8888")
if err != nil {
log.Println("dial error:", err)
return
}
defer conn.Close()
time.Sleep(1*time.Second)
buf := make([]byte, 1024)
n, err := conn.Write(buf)
log.Println(n, err)
}
客户端和服务端建立连接后,发送1024字节的数据包,服务端收到请求后,一直不处理数据,直到20s后退出,此时通过netstat查看
可以看到网络接收队列(recv-Q)里的数据一直阻塞没有被服务端处理。
解决思路
将服务改为多线程,加快服务端的处理速度。