xxx SYNs to LISTEN sockets dropped
$ netstat -s | grep -i "listen"
6006609 times the listen queue of a socket overflowed
6006609 SYNs to LISTEN sockets dropped
从 netstat -s 的输出可以看到,有 6006609 次 SYN 请求被丢弃,具体原因是 监听队列(listen queue)溢出。这表明你的服务器在高并发连接时,TCP 的 SYN 半连接队列(SYN backlog) 或 Accept 队列 已满,导致新的连接请求被丢弃。
问题原因
当客户端发起 TCP 连接(net.Dial)时,服务器会经历以下阶段:
- SYN 队列(半连接队列):客户端发送
SYN,服务器回复SYN-ACK,此时连接进入SYN_RECV状态,等待客户端ACK。 - Accept 队列(全连接队列):客户端发送
ACK后,连接进入ESTABLISHED状态,并进入 Accept 队列,等待服务器调用Accept()处理。
你的问题可能是:
- SYN 队列太小:服务器来不及处理
SYN请求,导致SYN被丢弃。 - Accept 队列太小:服务器
Accept()调用太慢,导致已建立的连接无法进入应用层,最终被丢弃。 - 服务器处理能力不足:Go 的
Accept()速度跟不上SYN请求的速度。
解决方案
1. 增大 TCP SYN 队列和 Accept 队列
Linux 通过 net.core.somaxconn 控制 Accept 队列的最大长度,而 Go 的 Listen() 默认使用该值。你需要:
# 查看当前 somaxconn 值(默认可能是 128 或 512)
sysctl net.core.somaxconn
# 临时增大(立即生效)
sudo sysctl -w net.core.somaxconn=65535
# 永久生效(写入 /etc/sysctl.conf)
echo "net.core.somaxconn=65535" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
在 Go 代码中显式设置 Backlog(建议):
ln, err := net.Listen("tcp", ":20007")
// 设置较大的 Backlog(建议 >= somaxconn)
if tcpLn, ok := ln.(*net.TCPListener); ok {
tcpLn.SetDeadline(time.Now().Add(1 * time.Second)) // 可选:设置 Accept 超时
}
2. 检查 SYN 队列是否溢出
# 查看 SYN 丢弃情况(和你的 netstat -s 输出一致)
netstat -s | grep -i "listen"
如果 SYNs to LISTEN sockets dropped 持续增长,说明 SYN 队列仍然太小。
调整 SYN 队列大小:
# 临时调整(默认可能是 128)
echo 8192 | sudo tee /proc/sys/net/ipv4/tcp_max_syn_backlog
# 永久生效
echo "net.ipv4.tcp_max_syn_backlog=8192" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
3. 优化服务器 Accept 性能
如果 Go 的 Accept() 处理太慢,会导致 Accept 队列堆积。可以:
- 增加
Accept的 Goroutine:for i := 0; i < runtime.NumCPU() * 2; i++ { go func() { for { conn, err := ln.Accept() if err != nil { log.Println("Accept error:", err) continue } go handleConn(conn) // 处理连接 } }() } - 使用
SO_REUSEPORT(Linux 3.9+):ln, err := net.Listen("tcp", ":20007") if err != nil { log.Fatal(err) } // 设置 SO_REUSEPORT(需要 Linux 内核支持) if tcpLn, ok := ln.(*net.TCPListener); ok { file, _ := tcpLn.File() syscall.SetsockoptInt(int(file.Fd()), syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1) }
4. 检查服务器资源限制
即使调整了队列,如果服务器 CPU/内存/文件描述符不足,仍然可能丢弃连接:
# 检查文件描述符限制
ulimit -n
# 检查系统全局 FD 限制
cat /proc/sys/fs/file-max
# 修改 limits.conf(如 65535)
echo "* soft nofile 65535" | sudo tee -a /etc/security/limits.conf
echo "* hard nofile 65535" | sudo tee -a /etc/security/limits.conf
5. 客户端优化
如果客户端短时间建立大量连接,可能会触发服务器的保护机制:
- 增加重试机制:
for i := 0; i < 3; i++ { // 最多重试 3 次 conn, err := net.Dial("tcp", "192.168.11.35:20007") if err == nil { break } time.Sleep(100 * time.Millisecond) } - 控制并发连接数(如使用
semaphore):sem := make(chan struct{}, 4000) // 限制并发 4000 for i := 0; i < 10000; i++ { sem <- struct{}{} go func() { defer func() { <-sem }() conn, err := net.Dial("tcp", "192.168.11.35:20007") if err != nil { log.Println("Dial error:", err) return } defer conn.Close() // 发送请求... }() }
总结
| 问题 | 解决方案 |
|---|---|
| SYN 队列溢出 | 增大 net.ipv4.tcp_max_syn_backlog |
| Accept 队列溢出 | 增大 net.core.somaxconn,Go 代码设置 Backlog |
| 服务器 Accept 处理慢 | 多 Goroutine Accept(),或 SO_REUSEPORT |
| 文件描述符不足 | 修改 limits.conf,增加 nofile |
| 客户端并发过高 | 增加重试,限制并发量 |
建议先调整 somaxconn 和 tcp_max_syn_backlog,再优化服务器 Accept 逻辑。如果问题仍然存在,可以结合 tcpdump 抓包分析具体丢包原因:
sudo tcpdump -i any port 20007 -nn -vv
Do not communicate by sharing memory; instead, share memory by communicating.

浙公网安备 33010602011771号