asyncio.wait_for()是Python异步超时控制的核心手段,它通过强制抛出TimeoutError并取消任务来实现真正中断,需配合aiohttp.ClientTimeout等细粒度配置使用。

Timeout 超时不是网络问题,而是你没给 asyncio 或 HTTP 客户端明确的等待边界——不设 timeout,等于让协程无限卡住。
asyncio.wait_for() 是最直接的超时控制方式
Python 原生 asyncio 不自动中断挂起的协程,asyncio.wait_for() 是唯一可靠手段。它在指定时间后强制抛出 asyncio.TimeoutError,且能真正取消底层任务(前提是被等待的对象支持取消)。
常见错误是只对 await 表达式加 timeout 参数(比如误以为 aiohttp.ClientSession.get(url, timeout=5) 就够了),其实那只是连接/读取阶段的限制,无法覆盖 DNS 解析、SSL 握手、或服务器无响应等全链路阻塞。
- 必须用
asyncio.wait_for(task, timeout=10)包裹整个请求协程 - timeout 值建议比客户端自身 timeout 大 1–2 秒,避免竞态
- 捕获
asyncio.TimeoutError,而不是通用Exception,否则会吞掉真正的异常
try:
resp = await asyncio.wait_for(session.get(url), timeout=12)
except asyncio.TimeoutError:
print("请求整体超时,已取消")
aiohttp.ClientTimeout 需要配合 session 级别配置
aiohttp.ClientTimeout 控制的是 HTTP 协议各阶段的细分超时,但它不会触发协程取消——如果 DNS 卡死或 TCP 连接 hang 住,ClientTimeout 可能完全不生效。
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
它的作用是让 session.get() 内部更早失败,减少 wait_for 的负担,但不能替代它。
- 必须通过
timeout=ClientTimeout(...)显式传入ClientSession构造函数,而非单次请求 -
total字段常被忽略:它限制整个请求生命周期(含重试),设为None会导致 timeout 失效 - 推荐组合:
connect=5,sock_read=8,total=10,再外层套wait_for(timeout=12)
timeout = aiohttp.ClientTimeout(
connect=5, # TCP 连接建立
sock_read=8, # socket 读取响应体
total=10 # 整个请求(含重试)上限
)
async with aiohttp.ClientSession(timeout=timeout) as session:
...
超时后连接未释放?检查 connector 的 limit 和 keepalive
即使抛出 TimeoutError,底层 TCP 连接可能仍处于 ESTABLISHED 状态,尤其当服务器迟迟不发 FIN。这会导致连接池耗尽、后续请求排队甚至 ClientOSError: [Errno 24] Too many open files。
- 务必设置
aiohttp.TCPConnector(limit=100, limit_per_host=30),避免单域名占满连接 - 启用
keepalive_timeout=30(默认 15s),让空闲连接更快回收 - 超时后手动调用
resp.close()或确保async with resp:语法被正确使用 - 生产环境建议加
force_close=True(关闭 keep-alive),牺牲复用换确定性
requests-async 已废弃,别踩坑
有人试图用 requests-async 这类第三方包“简化”异步请求,但它早已停止维护,底层仍是同步 requests + 线程池模拟,实际是伪异步,且 timeout 行为混乱、无法取消。
- 所有 timeout 参数在该库中均无效,最终退化为线程级
threading.Timer,不可靠 - 遇到
TimeoutError时连接不会释放,极易泄漏 - 唯一安全选择:原生
aiohttp或httpx.AsyncClient(后者 timeout 设计更直观)
超时逻辑最易错的地方,是把「某一段耗时」当成「整个请求生命周期」——DNS、TLS、TCP 重传、服务端队列,每个环节都可能独立 hang 住。没有外层 wait_for,就等于放弃控制权。

















