go tcp服务端最大连接数由操作系统资源决定,主要是ulimit -n(文件描述符上限)和内存,其中fd耗尽通常早于内存不足;需调高ulimit、配置kernel参数、用semaphore限流,并注意客户端并发控制与心跳机制。

Go TCP 服务端最大连接数由什么决定
不是 Go 语言本身限制,而是操作系统级资源瓶颈:主要是 ulimit -n(文件描述符上限)和内存。一个 TCP 连接至少占用 1 个 fd、几 KB 内存(socket buffer + goroutine stack),当并发连接达数万时,fd 耗尽比内存更早发生。
-
ulimit -n默认常为 1024,必须手动调高(如ulimit -n 65536),否则net.Listen后 Accept 会直接返回accept: too many open files - Go runtime 默认栈大小 2KB,每个连接一个 goroutine,10 万连接 ≈ 200MB 栈内存(不含 socket buffer),需确认系统内存充足
- Linux kernel 参数如
net.core.somaxconn(listen backlog)影响新连接入队能力,建议设为 ≥ 65535
用 semaphore 控制 accept 并发数最稳妥
别依赖 http.Server.MaxConns——标准库 http.Server 根本没有这个字段,这是常见误读。它只有 MaxIdleConns 等空闲连接参数,对新建连接零约束。
- 用
semaphore.NewWeighted在Accept后立即限流,拒绝时主动conn.Close(),避免 fd 泄漏 - 切忌在 goroutine 里调
sem.Acquire(ctx, 1)等待——连接已建立,等待期间 fd 已被占用,等于提前耗尽限额 -
sem.Release(1)必须在 goroutine 内用defer保证执行,panic 时也要释放
var sem = semaphore.NewWeighted(int64(maxConns))
ln, _ := net.Listen("tcp", ":8080")
for {
conn, err := ln.Accept()
if err != nil {
continue
}
if !sem.TryAcquire(1) {
conn.Close() // 拒绝连接,立刻释放 fd
continue
}
go func(c net.Conn) {
defer sem.Release(1)
handleConn(c)
}(conn)
}
测试连接上限要绕过 DNS 和超时干扰
用 net.Dial 批量建连时,opErr.Timeout() 或 syscall.ECONNREFUSED 是正常现象,但若大量出现 context.DeadlineExceeded,大概率是客户端 fd 耗尽或服务端 accept 队列满,而非网络问题。
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
- 批量 dial 必须控制并发,例如用带缓冲的 channel 或
semaphore限流,否则net.Dial失败会堆积 goroutine 导致 OOM - Windows 下判断拒绝要用
windows.WSAECONNREFUSED,Linux/macOS 用x/sys/unix.ECONNREFUSED,别硬写syscall.ECONNREFUSED(已弃用) - 连接成功后立即
conn.SetReadDeadline(time.Now().Add(1 * time.Second)),防止后续阻塞;不读不写也建议发个心跳包验证链路通断
真实压测时 keepalive 和心跳不能混用
开启 SetKeepAlive(true) 只是启用内核 TCP keepalive,Linux 默认 2 小时才探测,且 NAT 设备几乎必丢包;它无法替代应用层心跳,但可作为兜底。
- 长连接必须实现应用层 ping-pong:服务端定时
Write"PING",客户端收到后回 "PONG";连续 2 次无响应即关连接 -
SetReadDeadline必须每次读/写后重置,否则超时会误杀活跃连接;不要只在循环开头设一次 - 心跳包体积要小(如 4 字节二进制),避免触发 Nagle 算法延迟;禁用
conn.SetNoDelay(true)强制立即发送
真正卡住的地方永远不是代码逻辑,而是 ulimit 设置是否生效、sysctl 参数是否持久化、以及客户端 dial 并发没控住导致自己先崩。跑出 10 万连接前,先用 lsof -p $PID | wc -l 看服务端 fd 实际占用,比任何日志都准。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










