Go服务吞吐量低主因是HTTP超时与连接配置不当:ReadTimeout(建议5s)、WriteTimeout(建议10s)、IdleTimeout(必须设,如30s)和MaxHeaderBytes(建议64KB)四字段未显式配置,导致连接堆积、FD耗尽。

Go 服务吞吐量低,八成不是代码写得慢,而是连接没复用、超时没控好、goroutine 没管住——先别改业务逻辑,盯紧 http.Server 和 http.Transport 的几个关键字段。
http.Server 启动时必须显式设这 4 个超时
默认不设超时,一个慢请求就能卡住整个连接池,QPS 断崖下跌。现象是 netstat -an | grep :8080 | wc -l 高但 CPU 很低,ss -s 显示大量 ESTABLISHED 连接堆积。
-
ReadTimeout:从 TCP 建连到读完请求头的上限,建议5 * time.Second;大文件上传可单独用ReadHeaderTimeout -
WriteTimeout:从开始写响应到写完的上限,建议10 * time.Second,防 handler 卡在 DB 或日志里 -
IdleTimeout:Keep-Alive 空闲连接存活时间,必须设(如30 * time.Second),否则 fd 耗尽 -
MaxHeaderBytes:防攻击,设为64 (64KB)较安全
别再用 http.DefaultClient 发下游请求
它的 MaxIdleConnsPerHost 默认是 2,多域名高频调用时,连接池秒空,后续请求排队等连接,延迟毛刺明显。
- 每个下游目标应配独立
*http.Client实例,transport 中设MaxIdleConns≥ 200、MaxIdleConnsPerHost≥ 100 -
IdleConnTimeout设为60 * time.Second左右,太短失去复用价值,太长易积压失效连接 - 务必禁用
ExpectContinueTimeout(设为 0),小请求不会卡在100-continue等待上 - 全局复用 client 实例,禁止在 handler 里每次
new(http.Client)
goroutine 不是越多越好,得用池控住
每请求起一个 goroutine,在高并发下会迅速拉高 runtime.NumGoroutine(),调度器过载,P99 延迟飙升,pprof runtime.mallocgc 占比常超 40%。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 用
ants或自建 worker pool,初始大小按runtime.GOMAXPROCS(0) * 2 ~ 4压测调整 - 任务 channel 必须带缓冲(如
make(chan Task, 1000)),否则生产者一卡,吞吐直接归零 - 每个 worker 内必须用
select { case 主动响应取消,不能只靠超时 - DB 查询、HTTP 调用等 I/O 全部走
ctx,例如db.QueryContext(ctx, sql)
别忽略系统级参数和连接队列
Go 层面调优再细,/proc/sys/net/core/somaxconn 还是 128,客户端照样收 Connection refused。
- 启动前执行
sysctl -w net.core.somaxconn=4096,并确认cat /proc/sys/net/core/somaxconn输出一致 - 用
ss -lnt观察监听端口的Recv-Q,若持续接近somaxconn值,说明连接队列已满 - 若用
net.Listen("tcp", ...),拿到*net.TCPListener后无需手动 set backlog,系统参数生效即覆盖 - 容器环境注意 ulimit -n,避免 fd 不足导致 accept 失败
真正卡吞吐的地方,往往藏在 http.Transport.IdleConnTimeout 和 net.core.somaxconn 这种“不起眼”的配置里;改完记得用 ab 或 hey 对比压测,看 ss -s 和 runtime.NumGoroutine() 是否同步下降——数字不骗人。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










