默认http.server压测连接数上不去,因未设read/write/idletimeout导致fd被空闲连接占用,且go 1.19+默认http/2在无tls时因连接复用误判并发已满。

为什么默认 http.Server 在压测时连接数上不去
根本原因不是代码写得慢,而是默认配置把并发拦在了入口:http.Server 没设 ReadTimeout、WriteTimeout,底层 net.Listener 的 SetDeadline 不生效,导致空闲连接长期占着 file descriptor;更隐蔽的是,Go 1.19+ 默认启用了 HTTP/2,但没配好 TLS 或没关掉 HTTP/2 的连接复用,会让压测工具(如 hey 或 wrk)误判“并发已满”,实际是连接被复用卡住了。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 显式设置超时:
srv := &http.Server{ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, IdleTimeout: 30 * time.Second}——IdleTimeout尤其关键,它控制 keep-alive 连接空闲多久断开 - 如果不用 HTTPS,强制禁用 HTTP/2:
http2.ConfigureServer(srv, &http2.Server{})后立刻delete(http2ServeMux, srv)不现实;更稳妥的是在启动前加http2.DisableServerHTTP2 = true(Go 1.18 +) - 检查系统限制:
ulimit -n至少设为 65536;Linux 还要调/proc/sys/net/core/somaxconn和/proc/sys/net/core/netdev_max_backlog
http.ServeMux 是性能瓶颈吗?什么时候该换
不是它慢,是你用错了。默认 http.ServeMux 是线性遍历路由,当注册的路径超过 200 条、且大量带变量(如 /user/:id)时,每次请求都要字符串匹配+正则回溯,CPU 火焰图里 serveMux.Handler 会明显凸起。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 纯静态前缀路由(如
/api/v1/,/healthz)没问题;但只要出现:{name}或通配符*,就该换路由库 - 别直接上
gin或echo—— 它们自带中间件栈和反射,反而增加开销;优先试httprouter(零分配、Trie 路由)或 Go 1.22+ 原生支持的http.NewServeMux+HandleFunc分组 - 如果只是想提前拦截 /metrics 或 /debug/pprof,用
http.StripPrefix+ 单独http.ServeMux更轻量,别塞进主路由
怎么让 net/http 真正跑满 CPU 核心
Go 的 http.Server 本身是单 goroutine accept,但后续请求处理天然并发 —— 瓶颈往往出在「你主动加了锁」或「共享资源没拆分」。比如全局一个 sync.Pool 用于 JSON 编码,或所有 handler 共用同一个数据库连接池(*sql.DB),在高并发下会因 mutex 争抢变成串行。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 避免全局
sync.Pool:按业务域拆,比如jsonPoolForUser和jsonPoolForOrder,减少 false sharing -
*sql.DB的SetMaxOpenConns别设成runtime.NumCPU() * 4这种拍脑袋值;先用pg_stat_activity(PostgreSQL)或SHOW PROCESSLIST(MySQL)看真实连接堆积点,再调SetMaxIdleConns和SetConnMaxLifetime - 日志别用
log.Printf:它底层锁整个std实例;换成zerolog或zap的With+Info(),且确保Output是异步 writer(如os.Stderr配bufio.Writer)
为什么加了 pprof 后 QPS 反而掉了 30%
因为默认 net/http/pprof 注册的 handler 全部走主 http.ServeMux,而它的锁在高频访问 /debug/pprof/ 时会拖慢所有其他路由 —— 尤其当你用 curl 轮询 /debug/pprof/goroutine?debug=1 时,字符串匹配开销直接打穿。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 不要用
http.DefaultServeMux加pprof;单独起个监听地址:go http.ListenAndServe("127.0.0.1:6060", http.DefaultServeMux),然后http.DefaultServeMux只挂pprof相关 handler - 生产环境禁用
/debug/pprof/goroutine?debug=2和/debug/pprof/heap?debug=1—— 它们会触发 full GC,QPS 波动就是这么来的 - 需要持续 profiling?用
runtime/pprof.StartCPUProfile写文件,而不是靠 HTTP 接口拉取;内存 profile 用runtime.GC()后再WriteHeapProfile,避开运行时干扰
真正的高并发不靠堆参数,靠看清每个 http.Server 字段背后的操作系统语义,还有你代码里那些没声张的锁和共享变量。










