不能每次请求都新建asyncclient,因为会重复创建tcp连接池、无法复用keep-alive连接、易触发“too many open files”错误;应全局复用实例并合理配置limits和timeout。

httpx.AsyncClient 默认就带连接池,不需要手动创建;关键在复用实例、合理配置 timeout 和 limits。
为什么不能每次请求都 new 一个 AsyncClient?
每次 AsyncClient() 都会新建 TCP 连接池(底层是 httpcore.AsyncConnectionPool),导致:
- 重复建立/关闭 TCP 连接,浪费时间(尤其 HTTPS 握手)
- 无法复用 Keep-Alive 连接,服务端可能拒绝过多短连接
- 容易触发“too many open files”系统错误(每个 client 占多个 socket)
正确做法是:全局或作用域内只初始化一次 AsyncClient 实例,多次调用 .get() / .post()。
如何配置连接池大小和超时?
默认的 limits 偏保守(最多 10 个空闲连接、总共 100 个),高并发时会排队等待。常见调整项:
limits=httpx.Limits(max_connections=100, max_keepalive_connections=20)-
timeout=httpx.Timeout(10.0, connect=5.0, read=8.0)—— 分开设 connect/read 更可控 - 若目标域名固定,还可加
http2=True减少往返(但注意服务端是否支持)
示例:
import httpx <p>client = httpx.AsyncClient( limits=httpx.Limits(max_connections=50, max_keepalive_connections=30), timeout=httpx.Timeout(5.0, connect=3.0, read=4.0), ) </p>
async with AsyncClient() 什么时候该用?
仅在「单次短生命周期任务」中才用上下文管理器,比如命令行脚本、单元测试、一次性爬虫入口:
- 它会在退出时主动关闭连接池、释放 socket,避免资源泄漏
- 但如果你在 FastAPI 的
Depends或全局变量中用它,async with会导致每次请求都重建 client,反而更慢 - 长期运行服务中,应显式调用
await client.aclose()(如在 shutdown 事件里)
反模式示例(别这么写):
async def fetch(url):
async with httpx.AsyncClient() as client: # ❌ 每次都新建
return await client.get(url)
连接池不生效的典型信号
如果发现并发请求延迟高、响应时间波动大,先检查这些:
- 日志里反复出现
Connection pool is full, waiting for free connection→ 调大max_connections - 大量
ConnectTimeout或ReadTimeout→ 不是连接池问题,先看网络或服务端负载 - 用
client._transport._pool._connections(私有属性,仅调试)观察当前活跃连接数,确认是否复用成功 - 目标 URL 域名不一致(如
api.example.com和www.example.com)→ 连接池按 host:port 分隔,不会共享
真正要小心的不是怎么写连接池,而是让 client 实例活得足够久、且被所有协程共享——这比参数调优影响更大。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











