HTTPX的AsyncClient默认启用连接池并复用TCP连接,但必须复用同一client实例而非每次新建;通过httpx.Limits可调优max_connections、max_keepalive_connections和keepalive_expiry参数以适配高并发场景。

HTTPX 的 AsyncClient 默认就复用连接池
HTTPX 的 AsyncClient(以及同步版 Client)底层基于 httpcore,默认启用连接池,无需额外配置就能复用 TCP 连接。关键在于:**必须复用同一个 client 实例,不能每次请求都新建 client**。
常见错误是写成这样:
async def fetch(url):
async with httpx.AsyncClient() as client: # ❌ 每次都新建 client → 每次都建新连接池
return await client.get(url)这会导致连接无法复用,甚至触发“too many open files”错误。正确做法是将 client 提前创建、长期持有:
- 在 FastAPI/Starlette 中,通过 lifespan hook 或 dependency 管理 client 生命周期
- 在脚本或 CLI 工具中,用模块级变量或全局单例(注意线程/协程安全)
- 显式调用
client.aclose()(或await client.aclose())释放资源,避免连接泄漏
调整连接池大小:limits 参数控制并发与复用能力
HTTPX 不叫“长连接池”,而是通过 httpx.Limits 控制连接复用的上限和行为。它影响三个维度:总连接数、每 host 并发数、空闲连接保活时长。
立即学习“Python免费学习笔记(深入)”;
默认值(httpx.Limits())通常够用,但高并发场景需显式调优:
-
max_connections=100:整个 client 允许的最大活跃连接数(含空闲) -
max_keepalive_connections=20:最多保留多少空闲连接等待复用 -
keepalive_expiry=60.0:空闲连接最长保留秒数(超时后关闭)
示例:
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
client = httpx.AsyncClient(
limits=httpx.Limits(
max_connections=200,
max_keepalive_connections=50,
keepalive_expiry=30.0
)
)注意:keepalive_expiry 太小(如 1s)会让连接很快被关掉,失去复用意义;太大(如 300s)可能在服务端主动断连后,客户端还傻等,下次复用时触发 ConnectionNotAvailable 异常。
遇到 ConnectionNotAvailable 怎么办
这个异常不是报错,而是连接池已满或空闲连接过期后的正常反馈,说明 HTTPX 没法立刻拿出一个可用连接 —— 常见于突发流量或连接池配置过紧。
应对方式不是重试,而是检查以下几点:
- 是否误在循环内反复创建
AsyncClient?确认 client 是共享实例 - 是否漏掉
await client.aclose()?未关闭的 client 会持续占用连接句柄 - 服务端是否主动关闭了连接(如 Nginx 的
keepalive_timeout设为 10s,而 client 的keepalive_expiry是 30s)?此时需对齐两端保活时间 - 是否发起太多并发请求超出
max_connections?可临时加大限制,或加限流(如anyio.Semaphore)
同步 client 也支持连接池,但要注意线程安全
httpx.Client 同样默认启用连接池,但它不是协程安全的,**不能跨线程共享**。如果你在多线程环境(如 Flask + threading)中使用,每个线程应持有自己的 Client 实例,或用线程局部存储(threading.local)封装。
而 AsyncClient 是协程安全的,可在同一线程内的多个协程间安全共享 —— 这也是推荐异步服务统一用 AsyncClient 的主要原因。
连接池本身不区分 HTTP/1.1 和 HTTP/2,但复用前提是你访问的是同一 host+port 且协议协商一致;如果服务端不支持 HTTP/2,即使你设了 http2=True,也会自动降级,不影响池化逻辑。

















