新闻详情

新闻详情

首页 / 资讯中心 / 详情

​编辑 Go 的 http.Client 到底有几层超时:设了 Timeout 为什么请求还是会卡住​​​​​​

发布时间:2026/9/30 4:47:22来源:尧图网络
​编辑 Go 的 http.Client 到底有几层超时:设了 Timeout 为什么请求还是会卡住​​​​​​
一个很常见的线上现象调用第三方接口的 goroutine 卡了十几分钟才返回而代码里明明给http.Client设了Timeout: 10 * time.Second。这类问题查到最后往往是那个请求根本没走你以为的那个 client。但要讲清楚为什么得先把net/http客户端的超时体系过一遍它比大多数人以为的要分层得多。这篇把每一层是什么、管哪一段、不设会怎样讲清楚。一次请求的时间线一个 HTTPS 请求从发起到读完响应大致经过这几段DNS 解析 → TCP 建连 → TLS 握手 → 写请求 → 等响应头 → 读响应体net/http对这些阶段分别有控制点散落在三个地方http.Client、http.Transport、net.Dialer。另外还有贯穿全程的context。第一层Client.Timeout管全程client : http.Client{Timeout: 10 * time.Second}这是最粗的一刀从发起请求到读完响应体整个过程超过 10 秒就取消。包括重定向包括读 body。最后一点经常被忽略。client.Do返回的时候body 还没读计时器还在走。如果你拿到resp之后慢吞吞地处理再去io.ReadAll(resp.Body)可能会在读 body 时收到context deadline exceeded (Client.Timeout or context cancellation while reading body)。它的问题是太粗下载一个大文件10 秒不够调一个本该 50 毫秒返回的接口10 秒又太长。而且它不区分连不上和对方处理慢这两种情况的应对通常不一样。默认值是 0也就是永不超时。http.Get、http.Post用的http.DefaultClient就是这个状态。开头说的那种设了超时还是卡住十有八九是某个工具函数里直接用了http.Get。第二层Transport 上的分阶段超时transport : http.Transport{ DialContext: (net.Dialer{ Timeout: 3 * time.Second, // TCP 建连 KeepAlive: 30 * time.Second, }).DialContext, TLSHandshakeTimeout: 3 * time.Second, // TLS 握手 ResponseHeaderTimeout: 5 * time.Second, // 写完请求后等响应头 ExpectContinueTimeout: 1 * time.Second, IdleConnTimeout: 90 * time.Second, // 空闲连接在池里留多久 MaxIdleConnsPerHost: 20, } client : http.Client{Transport: transport, Timeout: 30 * time.Second}逐个说Dialer.TimeoutTCP 三次握手的上限包括 DNS 解析。对方机器挂了、防火墙丢包不回 RST不设这个会等到操作系统的 SYN 重试耗尽Linux 上默认是一两分钟。TLSHandshakeTimeoutTCP 连上之后 TLS 握手的上限。ResponseHeaderTimeout请求发完之后等对方返回响应头的时间。这个最能区分对方处理慢。它不管读 body 的时间。IdleConnTimeout和请求耗时无关是连接池里空闲连接的存活时间。设得比服务端的 keep-alive 超时短能减少复用了一条对方已经关掉的连接导致的EOF/connection reset报错。这样配完连不上会在 3 秒左右失败对方处理慢会在 5 秒失败而一个正常开始返回、只是 body 很大的下载可以一直读到Client.Timeout的 30 秒。注意http.DefaultTransport本身已经设了Dialer.Timeout: 30s、TLSHandshakeTimeout: 10s但没有设ResponseHeaderTimeout。所以只用默认 transport、又不设Client.Timeout的话对方接了连接但一直不回请求就会一直挂着。第三层context管单次调用ctx, cancel : context.WithTimeout(r.Context(), 2*time.Second) defer cancel() req, _ : http.NewRequestWithContext(ctx, http.MethodGet, url, nil) resp, err : client.Do(req)Client.Timeout是 client 级别的所有请求共用一个值。而 context 可以每次调用单独定并且可以从上游继承上面用了r.Context()如果调用你的那个 HTTP 请求被客户端断开了这个下游请求也会跟着取消不会白跑。context 和Client.Timeout同时存在时谁先到用谁。一个常见的错func fetch(url string) (io.ReadCloser, error) { ctx, cancel : context.WithTimeout(context.Background(), 2*time.Second) defer cancel() req, _ : http.NewRequestWithContext(ctx, http.MethodGet, url, nil) resp, err : client.Do(req) if err ! nil { return nil, err } return resp.Body, nil // 错返回了 body但函数一退出 cancel 就执行了 }defer cancel()在函数返回时执行context 被取消调用方再去读 body 会报context canceled。要么在函数内读完 body 再返回[]byte要么把 cancel 交给调用方比如包一层ReadCloser在Close里调 cancel。第四层服务端也有一套别混了写到这里容易和http.Server的超时混在一起srv : http.Server{ ReadHeaderTimeout: 5 * time.Second, ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, }这是你作为服务端时防慢客户端的。ReadHeaderTimeout尤其重要不设的话一个连上来之后一个字节一个字节慢慢发请求头的客户端slowloris就能长时间占着连接。它和客户端那套没有关系只是名字像。回到开头那种卡住一个典型的排查顺序goroutine dump/debug/pprof/goroutine?debug2里看到卡住的栈停在net/http.(*persistConn).roundTrip说明是在等响应不是在建连往上翻调用链发现走的是一个工具函数里的http.Get用的是DefaultClient没有任何超时对方服务接了连接但迟迟不回响应头发布中、线程池打满都可能而DefaultTransport没有ResponseHeaderTimeout于是一直等。修法很朴素全局禁止直接用http.Get/http.DefaultClient统一走一个配好超时的 client并且调用处一律用带 context 的请求。可以在 CI 里加一条 grep 检查http.Get(/http.Post(/http.DefaultClient比靠 code review 记得住靠谱。一张速查表配置在哪管哪一段默认TimeoutClient全程含读 body0不限Dialer.TimeoutTransport.DialContextDNS TCP 建连DefaultTransport 为 30sTLSHandshakeTimeoutTransportTLS 握手DefaultTransport 为 10sResponseHeaderTimeoutTransport写完请求到收到响应头0不限IdleConnTimeoutTransport空闲连接存活DefaultTransport 为 90scontext deadline每个 Request单次调用全程无局限这篇只讲了标准库的行为用了第三方 HTTP 库resty 之类的话它们的超时参数最终也是落到这几个字段上但默认值各不相同要去看具体库的文档。另外 HTTP/2 下一条连接上多路复用多个请求连接池相关参数MaxIdleConnsPerHost等的意义和 HTTP/1.1 不同这里没展开。福兮forxi.cn上的网页截图、IP 查询这类功能都要调外部服务写这类代码时这几层超时是最先要想清楚的事。文中的数值只是示例没有标准答案要按每个下游的实际响应时间来定关键是每一层都要有值别留 0。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

从《云计算导论》到实战:IaaS、PaaS、SaaS 与运维避坑指南 2026/9/30 5:53:11

从《云计算导论》到实战:IaaS、PaaS、SaaS 与运维避坑指南

简介:这份《云计算导论》文档面向计算机专业学生、IT从业者及希望系统了解云计算基础概念的学习者,帮助读者从零建立对云计算定义、技术原理与产业影响的整体认知。资源为单个doc文档,压缩包约61KB,内容以章节化讲义形式组织&…

阅读更多 →
2800张手机检测YOLO数据集:从采集标注到训练实测的完整指南 2026/9/30 5:53:11

2800张手机检测YOLO数据集:从采集标注到训练实测的完整指南

最近把手上的手机检测项目做完了,顺手把整理好的2800张YOLO格式数据集共享了出来。做目标检测的人应该都有体会:找公开数据集最痛苦的不是“找不到”,而是“找到了但不好用”——要么标注格式不统一,要么图片质量参差不齐&#xf…

阅读更多 →
AI生成代码可读性差?三招让它从能跑变成能维护 2026/9/30 5:53:11

AI生成代码可读性差?三招让它从能跑变成能维护

真正尝试过AI生成代码的团队,几乎都会在“可读性”上栽过跟头:代码能跑,但代码审查变成考古,后续维护变成一笔隐形负债。AI生成代码的可读性,从来不是什么锦上添花,而是直接决定项目能不能被团队长期掌控的…

阅读更多 →
CNN注意力机制:SE、ECA、CBAM的PyTorch实现与ResNet实战 2026/9/30 5:53:11

CNN注意力机制:SE、ECA、CBAM的PyTorch实现与ResNet实战

做图像分类的模型跑不动、涨点慢的时候,我第一个想到的补丁往往不是换 backbone,而是往卷积块里塞一个轻量的注意力模块。CNN 的注意力机制这几年已经攒下了一大批成熟实现,SE、ECA、CBAM 是最常用的三个,代码短、参数少、几乎不用…

阅读更多 →
AI+CAD工程化落地:从DXF/DWG解析到FreeCAD自动化实践 2026/9/30 5:53:11

AI+CAD工程化落地:从DXF/DWG解析到FreeCAD自动化实践

1. 从Demo到工程:AI与CAD结合的真实困境做过CAD相关开发的人都有一个共同感受:演示视频里AI自动生成图纸、自动标注、自动改图,看起来无所不能,但一旦落到真实工程项目里,几乎处处碰壁。我自己在过去两年里&#xff0c…

阅读更多 →
SoL-Pi兼容与部署完全参考:如何选对并验证Pi版本 2026/9/30 5:53:05

SoL-Pi兼容与部署完全参考:如何选对并验证Pi版本

SoL-Pi兼容与部署完全参考:如何选对并验证Pi版本 【免费下载链接】SoL-Pi SoL-Pi: Scaling Auto-Research Loops for Efficient Agent Harnesses 项目地址: https://gitcode.com/gh_mirrors/so/SoL-Pi SoL-Pi 是 Pi 编码代理的独立效率扩展,安装前…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉