用 httpx.AsyncClient 配合分阶段 timeout 和 AsyncHTTPTransport 重试,比 requests 更省资源、更可控;需拆解 connect/read/write 超时,重试应在 transport 层实现并区分错误类型。

直接上结论:用 httpx.AsyncClient 配合 timeout 参数和 AsyncHTTPTransport 的重试策略,比 requests 同步重试更省资源、更可控;但别只设单个超时值,必须拆开管连接、读取、写入三阶段。
为什么不用 requests 做异步超时重试?
requests 本质是同步库,硬套 asyncio.to_thread 或 loop.run_in_executor 只是“假装异步”——线程池仍会卡住、无法真正中断阻塞调用、重试时序难控。而 httpx 原生支持 async/await,超时可精确到毫秒级中断,且 transport 层能拦截并重发失败请求。
-
requests的timeout=5在异步上下文中会阻塞整个 event loop,不是真异步 -
httpx的timeout=httpx.Timeout(5.0, connect=3.0, read=10.0, write=2.0)各阶段独立生效,不互相拖累 - 重试逻辑在 transport 层实现,失败后自动重建连接,不污染业务代码
如何配置 httpx.AsyncClient 的分阶段超时?
超时不是越长越好,而是要按网络链路分段控制:连接慢说明 DNS 或 TCP 握手有问题,读取慢大概率是服务端处理卡顿或响应体太大,写入慢则常见于大 payload 上传场景。生产环境建议显式拆解:
- 连接超时(
connect):设为 2–5 秒,国内服务建议 3 秒,跨境 API 建议 5–8 秒 - 读取超时(
read):大模型或文件类接口建议 30–60 秒,普通 REST 接口 5–15 秒 - 写入超时(
write):上传大于 1MB 数据时必须设,否则小包重传可能无限卡在 send() 阶段 - 总超时(
timeout):应略大于各阶段之和,避免被意外截断
示例配置:
立即学习“Python免费学习笔记(深入)”;
import httpx <p>timeout = httpx.Timeout( timeout=45.0, # 总时限兜底 connect=5.0, # 连接阶段上限 read=30.0, # 读响应上限 write=10.0 # 发送请求体上限 )
怎么让 httpx 异步请求自动重试?
httpx 不像 requests 那样内置 retry adapter,得靠 AsyncHTTPTransport + 自定义 RetryPolicy。关键点是:重试不能无脑叠加,必须区分错误类型、限制次数、加入退避间隔,否则容易触发对方限流。
- 只对
ConnectError、ReadTimeout、WriteTimeout和 5xx 状态码重试,4xx(如 400、401)直接失败 - 用
backoff_factor实现指数退避,第 1 次重试等 1 秒,第 2 次等 2 秒,第 3 次等 4 秒 - 设置
max_redirects=0,避免重试时被 302 重定向放大请求量 - transport 必须挂载到 client 实例,不能只改默认 session
最小可用重试 client 示例:
import httpx from httpx import AsyncHTTPTransport <p>transport = AsyncHTTPTransport( retries=3, verify=True, http2=True, )</p><h1>注意:retries 参数仅在 httpx>=0.27.0 中原生支持,旧版需手动 wrap</h1><p>client = httpx.AsyncClient( transport=transport, timeout=httpx.Timeout(connect=5.0, read=30.0, write=10.0), )
最容易被忽略的坑:异步超时 + 重试组合下的资源泄漏
async with 语法糖不是万能的——如果超时发生在 await client.post(...) 内部,而你没在 except 块里显式调用 await client.aclose(),连接可能滞留在 transport 的连接池中,几次重试后就耗尽 pool_size。
- 永远用
try/except/finally包裹 async client 调用,确保await client.aclose()执行 - 不要复用全局
AsyncClient实例做高并发请求,它不是线程安全的,应按 scope 生命周期管理(如 FastAPI 的 dependency) - 启用
httpx.HTTPStatusError捕获非 2xx 响应,否则 503 错误会被当成成功返回,重试机制完全失效
真正的稳定不是靠堆重试次数,而是让每次请求都清楚自己在哪一环失败、该等多久、要不要换 endpoint——这些细节藏在 timeout 拆分和 transport 配置里,而不是日志里一句 “retrying…”。


















