记一次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)里的数据一直阻塞没有被服务端处理。

 

 

解决思路

 将服务改为多线程,加快服务端的处理速度。

 

posted @ 2021-09-19 16:07  独揽风月  阅读(208)  评论(0编辑  收藏  举报