必须显式设置timeout参数,否则requests默认无限等待;推荐用元组timeout=(connect, read)如(3,10),并结合urllib3.retry实现带退避的可控重试。

TimeoutError 不是挂起,而是超时后抛出的异常;真正卡住 30 秒,说明你没设读取超时(read timeout),Python 默认用的是底层 socket 的无限等待。
requests 没设 timeout 参数时到底在等什么?
requests 底层调用 urllib3,而 urllib3 在未显式传入 timeout 时,会把 socket 的 recv() 调用设为阻塞模式——即一直等到服务端发来数据或 TCP 连接被对端关闭。某些 API(比如大模型流式响应、gRPC-Web 长轮询)可能只发 header 就停住,后续 body 延迟几十秒才推送,这时 Python 就傻等。
- 连接成功 ≠ 响应完成:TCP 握手和 TLS 握手完成后,
response.content或response.json()才真正触发读取 - 默认行为不等于安全行为:requests 0.x 到 2.x 全系都无全局默认 timeout,全靠你手动填
-
timeout=30是单值写法,等价于timeout=(30, 30),但连接和读取场景不同,混用容易误判
为什么只设连接超时(connect timeout)救不了命?
连接超时只管“连上”,不管“收完”。常见错误写法:timeout=5 看似激进,但一旦 TCP 连通、TLS 握手完成,后续就彻底放任不管——哪怕服务端卡在中间件鉴权、数据库慢查询、或模型推理队列里,Python 也会干等下去。
- 典型症状:日志显示
Starting new HTTPS connection很快,但response = ...行卡死 30 秒 - 真实瓶颈常在服务端业务逻辑,而非网络层;客户端必须主动设读取上限
- OpenAI / Seedance / 阿里云百炼等大模型 API,返回延迟波动大,必须拆开设
(connect, read)
httpx 和 aiohttp 的 timeout 行为更隐蔽
httpx.AsyncClient 和 aiohttp.ClientSession 的 timeout 默认值也是 None,且协程下更容易被忽略——因为 await 本身不报错,只是永远 suspend。
-
httpx.AsyncClient(timeout=30)同样只设总 timeout,不区分阶段;推荐显式用httpx.Timeout(connect=5.0, read=60.0) - aiohttp 中
timeout参数类型是aiohttp.ClientTimeout,直接传数字会静默转成总 timeout,易漏掉 read 配置 - uvloop 下若服务端发 FIN-ACK 后迟迟不发 payload,协程可能 stuck 在
await resp.text(),Wireshark 可见包已到,但 Python 未触发回调(见 uvloop 层定位)
DNS 和代理拖慢的不是“请求”,而是“发起请求前的 30 秒”
如果卡在 requests.get(...) 第一行不动,大概率是 DNS 解析或代理握手环节。requests 默认用系统 getaddrinfo(),没缓存、没超时控制,遇到坏 DNS 服务器就卡满 30 秒(glibc 默认重试 + 超时策略)。
- 验证方法:用
time nslookup api.example.com或python -c "import socket; print(socket.gethostbyname('api.example.com'))"单独测 DNS - 解决方案:用
requests.adapters.HTTPAdapter配pool_connections和pool_maxsize复用连接,或换httpx(自带异步 DNS 解析) - 代理配置错误时(如
https://请求走 http 代理),requests 会卡在 CONNECT 隧道建立阶段,同样无 timeout 控制
真正的麻烦不在超时值设多大,而在你根本没意识到:Python 的 HTTP 客户端默认不设限——它信任服务端会在“合理时间”内响应,而这个“合理”,从来不是 30 秒。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











