go语言默认http.server无法支撑千万级http持久连接,因其为短连接设计,idletimeout未设、读写缓冲区仅4kb、goroutine无节制增长、客户端maxidleconnsperhost过小等导致5–10万连接即出现拒绝、泄漏或gc卡顿。

Go 语言能支撑千万级 HTTP 持久连接,但前提是不做默认配置直跑——http.Server 默认参数在高并发长连接场景下会迅速成为瓶颈,比如 IdleTimeout 过短、MaxConnsPerHost 未设、读写缓冲区太小、协程无节制增长等。真实压测中,不调优的 Go HTTP Server 往往在 5–10 万连接时就开始出现连接拒绝、goroutine 泄漏或 GC 频繁卡顿。
为什么默认 http.Server 带不动千万连接
Go 的 net/http 默认为「短连接友好」设计:它假设请求快进快出,没为长连接生命周期做深度优化。几个关键点直接限制规模:
-
IdleTimeout默认是 0(即不限制空闲时间),但实际中若不显式设为合理值(如 60–300 秒),会导致连接长期挂起、占用 fd 和内存,最终触发too many open files -
ReadBufferSize和WriteBufferSize默认仅 4KB,高频小包场景下系统调用次数爆炸,CPU 花在 syscall 上远多于业务处理 - 每个连接启动一个 goroutine,没有池化或限流机制;当连接数达百万级,goroutine 数量同步膨胀,调度器压力陡增,GC mark 阶段延迟明显升高
-
http.Transport的客户端侧MaxIdleConnsPerHost若未调大(如设为 10000+),服务端即使支持长连接,客户端也会主动断连重连
必须调整的 4 个核心 Server 参数
这些不是“可选优化”,而是千万连接的前提配置项。漏掉任意一个,都可能让服务在 20 万连接后开始抖动:
-
IdleTimeout:设为300 * time.Second,避免连接空转耗资源;同时配ReadTimeout/WriteTimeout防止单连接阻塞整个 goroutine -
ReadBufferSize和WriteBufferSize:建议统一设为64 * 1024(64KB),平衡内存占用与 syscall 开销;实测在消息推送类场景中,比默认值降低约 37% 的 CPU 占用 -
ConnState回调:务必注册,用于跟踪StateNew/StateClosed/StateIdle状态,配合原子计数器做连接数实时监控,否则无法感知连接泄漏 -
Handler内必须用context.WithTimeout或context.WithCancel包裹业务逻辑,防止某个长连接上的 handler 卡死导致 goroutine 永久悬挂
操作系统层绕不过的三道坎
Go 再快,也得跑在 OS 上。千万连接意味着至少 100 万+ 文件描述符,Linux 默认配置根本不允许:
- 用户级限制:
ulimit -n 1048576必须生效(写入/etc/security/limits.conf并确认登录 session 加载) - 内核级
net.core.somaxconn至少设为65535,否则accept()队列溢出,新连接被静默丢弃,现象是客户端偶发connection refused - TCP 参数要调:
net.ipv4.tcp_fin_timeout=30缩短 TIME_WAIT 持续时间;net.ipv4.tcp_tw_reuse=1允许复用处于 TIME_WAIT 的 socket;否则大量连接卡在 TIME_WAIT,耗尽端口
别碰 net/http 的底层连接管理
想自己接管 conn 生命周期?比如读取原始 net.Conn、手动 SetKeepAlive、甚至用 http.Hijack() —— 这类操作在千万连接规模下极易引发竞态和内存泄漏。官方 http.Server 的连接复用逻辑已足够健壮,真正该做的,是用好它暴露的钩子(ConnState、RegisterOnShutdown)和超时控制,而不是绕过它。
真正复杂的地方不在代码怎么写,而在于你能否把每万连接的内存增长压到 20MB 以内、能否让 GC pause 控制在 1ms 内、以及是否在连接数突增时,有手段快速定位是哪类客户端(iOS/Android/浏览器)在批量重连——这些,靠改几个参数解决不了,得靠持续采集 runtime.ReadMemStats、net/http/pprof 和连接状态机日志。











