client := &http.Client{Timeout: 3 * time.Second}
tr := &http.Transport{}
tr.ResponseHeaderTimeout = 2 * time.Second
go test -run TestClientTimeout ./...

下游接口偶尔卡住不返回,本服务的 worker 很快被占满,队列堆到几千,日志要等到一行 `context deadline exceeded` 才继续输出,中间那两分钟什么都没记下来。这类现象通常不是突然出现的,往前翻半天日志能看到苗头。

追下去是这么回事:http.Client 零值没有总体超时,连接能建上就一直等,下游不返回也不断开,单个卡住的请求会长期占着一个 worker,并发一多池子就被耗尽。

「http.Client 超时」排查到最后往往是很小的一处差异,前面那些复杂猜测可以先放一放。权限不要一口气开到最大,先加到够用再往上试,范围小才好收。

把复现步骤压到三条以内,交给同事也能跑出来,才算真的定位清楚。

处理上按这个顺序:给客户端设置整体超时而不是只设连接超时;按接口分别设置超时时间并且限制重试次数;下游变慢时快速失败,不要让请求在队列里无限排队。

确认没问题再收工,下游卡住时请求三秒内返回错误,worker 数不再跟着错误一起上涨。

作者 9823189