默认参数拖后腿是因为 maxidleconns=100、maxidleconnsperhost=2 过于保守,导致高并发下连接复用率低、频繁重建连接,引发握手开销和超时错误。

长连接下 net/http 的默认连接池参数为什么拖后腿
net/http 的 http.Transport 默认对长连接极其保守: MaxIdleConns 是 100,MaxIdleConnsPerHost 是 2,IdleConnTimeout 是 30 秒。这在压测或高并发网关场景中,会直接导致连接复用率极低——大量请求被迫新建 TCP 连接,触发三次握手、TLS 握手开销,甚至出现 http: TLS handshake timeout 或 dial tcp: i/o timeout 等错误,实际是连接池耗尽后的重试堆积,而非网络问题。
实操建议:
- 显式初始化
http.Transport,把MaxIdleConns设为 200–500(视后端实例数和 QPS 调整) -
MaxIdleConnsPerHost至少设为后端单节点的预期并发连接数(例如 50),避免被单 host 限制卡死 -
IdleConnTimeout建议 30–60 秒;太短(如 5 秒)会导致连接频繁重建,太长(>120 秒)易积累 stale 连接 - 务必禁用
ExpectContinueTimeout(设为 0),否则小 POST 请求会在 100-continue 上阻塞 1 秒
fasthttp 的长连接能力靠什么撑住百万级并发
fasthttp 不依赖传统连接池,而是用 workerPool + sync.Pool 复用整个处理上下文:RequestCtx、reader、writer 全部池化,连接由固定数量的 goroutine worker 轮询处理,无 per-connection goroutine 开销。它默认支持 keep-alive,且不主动关闭空闲连接——只要客户端不发 Connection: close,连接就一直保留在 worker 持有的 net.Conn 上,复用成本趋近于零。
但注意:
- 它默认不限制最大并发连接数(底层靠 OS 文件描述符和
net.Listen的 backlog 控制),需自行通过Server.Concurrency或外层限流控制 - 没有内置的连接老化机制,长时间 idle 的连接可能被中间设备(如 NLB、iptables)静默断开,需配合应用层心跳或
TCPKeepAlive设置 -
Server.ReadTimeout和Server.WriteTimeout必须显式设(默认 0,即永不超时),否则异常连接会永久占用 worker
真实吞吐对比中容易被忽略的测量陷阱
压测时若只看“QPS”或“平均延迟”,很容易误判。net/http 在低并发(
关键观测点:
- 用
pprof看runtime.mallocgc占比:net/http 超过 40% 通常意味着Header、bytes.Buffer分配成为瓶颈;fasthttp 应接近 0 allocs/op - 用
ss -s或netstat -an | grep :8080 | wc -l确认 ESTABLISHED 连接数是否真实达到预期,而非被连接池截断 - 客户端必须复用
http.Client实例(尤其用 net/http 调用 fasthttp 后端时),否则new(http.Client)会绕过 Transport 复用逻辑
什么时候该坚持用 net/http,而不是换 fasthttp
不是所有“长连接高吞吐”场景都适合 fasthttp。如果你的应用需要:
- HTTP/2 支持(gRPC、server push)→ fasthttp 不支持
- 标准
http.Handler生态(chi、gorilla/mux、middleware 如 logging/tracing/auth)→ fasthttp 需重写适配,无兼容层 - 流式响应(SSE、chunked transfer encoding 动态写)→ fasthttp 的
ctx.SetBodyStreamWriter可用,但语义和调试体验远不如 net/http 的ResponseWriter - 严格的 Host 头校验、Referer 检查、自动 redirect 跟踪 → fasthttp 全部需手动实现
最常被低估的一点:fasthttp 的 RequestCtx.URI().QueryString() 返回的是底层缓冲区的 []byte 切片视图,直接转成 string 并长期持有,下次请求来时内存已被覆盖——这是典型的 use-after-free,线上偶发乱码或 panic 的根源。











