本文目录导读:

HTTPX 是一个支持异步的 HTTP 客户端库,其异步性能表现非常优秀,但具体表现取决于你如何使用它。
HTTPX 的异步性能与著名的 aiohttp 相当,甚至在某些场景下更优,同时提供了更现代、更简洁的 API 和更丰富的功能。
下面我们从几个维度详细分析 HTTPX 的异步性能表现:
核心优势:基于优秀底层
- 底层依赖:HTTPX 的异步后端完全基于
anyio(通过httpcore库实现)。anyio是一个强大的异步底层库,它提供了与asyncio(Python 原生异步库)和trio这两个异步框架的兼容性。 - 传输层:
httpcore负责底层的连接池、连接复用、超时控制等,这些是影响并发性能的关键,HTTPX 在连接池管理、复用、超时方面做得非常成熟和高效。
性能对比:HTTPX vs aiohttp vs requests
这是大家最关心的问题,我们以一个常见的场景为例:发送 1000 个并发 HTTP 请求(例如抓取 API 或网页)。
| 特性 | requests (同步) |
aiohttp (异步) |
httpx (异步) |
|---|---|---|---|
| 并发模型 | 阻塞式,串行 | asyncio 事件循环 |
anyio / asyncio 事件循环 |
| 处理 1000 请求耗时 | 非常慢(约 100-200 秒) | 非常快(约 2-5 秒) | 非常快(约 2-5 秒) |
| 连接池复用 | 每次请求新建连接 | 自动复用 | 自动复用 |
| 内存占用 | 较低(单线程) | 中等(多个协程) | 中等(多个协程) |
| API 简洁性 | 最简洁直观 | 较复杂,需管理 ClientSession |
与 requests 几乎一致,非常简洁 |
| 底层库 | urllib3 |
自研底层 | httpcore + anyio |
- 与
requests相比:在并发场景下,HTTPX 异步版本的速度提升了 50-100 倍。requests根本不适合做大规模并发请求。 - 与
aiohttp相比:两者在纯并发性能上非常接近,通常差距在 5% 以内,HTTPX 的代码可读性和易用性明显更好(因为它模仿了requests的 API),而aiohttp在极端优化和底层自定义方面更灵活。 - 与
httpx同步版本相比:HTTPX 的异步版本比其同步版本快几个数量级,如果你的代码是 I/O 密集型的(比如大量网络请求),务必使用异步版本。
实际性能影响因素
虽然 HTTPX 底层很快,但最终性能还取决于以下几个因素:
-
并发数:HTTPX 通过
asyncio.gather()或Semaphore控制并发,默认情况下,连接池有连接数限制(如 10 个),你需要调整limits参数:import httpx import asyncio limits = httpx.Limits(max_keepalive_connections=5, max_connections=100) # 最大并发连接100 async with httpx.AsyncClient(limits=limits) as client: ...如果并发数超过连接池上限,请求会排队等待,导致性能下降。
-
目标服务器限制:如果你并发请求同一个网站,目标服务器可能会限流(返回 429 状态码),此时再快的客户端也没用。
-
网络延迟:对于高延迟连接(如跨洲访问),异步能很好地并发等待,性能提升巨大,对于低延迟本地连接,提升较小。
-
连接复用:HTTPX 自动支持 HTTP/1.1 Keep-Alive 和 HTTP/2,使用 HTTP/2 可以在单个 TCP 连接上并发发送多个请求,显著提升性能。
# 启用 HTTP/2 async with httpx.AsyncClient(http2=True) as client: ...注意:HTTP/2 通常能带来 10-30% 的性能提升,尤其是需要发送大量小请求时。
-
编码与序列化:HTTPX 在读取响应时,默认会进行自动解码(如 gzip、deflate),这部分开销很小,但如果你不需要(比如下载二进制文件),可以关闭以节省 CPU:
async with httpx.AsyncClient(default_encoding=False) as client: ...
代码示例:100 个并发请求性能测试
import asyncio
import httpx
import time
async def make_request(client, url):
try:
response = await client.get(url)
return response.status_code
except httpx.RequestError as e:
return str(e)
async def main():
url = "https://httpbin.org/delay/1" # 模拟慢速服务器
num_requests = 100
limits = httpx.Limits(max_connections=50) # 控制并发数
async with httpx.AsyncClient(limits=limits, http2=True) as client:
start_time = time.perf_counter()
tasks = [make_request(client, url) for _ in range(num_requests)]
results = await asyncio.gather(*tasks)
end_time = time.perf_counter()
print(f"结果:{len(results)} 个请求完成")
print(f"耗时:{end_time - start_time:.2f} 秒")
print(f"QPS:{num_requests / (end_time - start_time):.1f} 请求/秒")
# 运行
asyncio.run(main())
预期输出(以上述代码为例,网络环境良好):
结果:100 个请求完成
耗时:2.05 秒
QPS:48.8 请求/秒
(如果不用异步,100 个串行请求耗时约 100 秒。)
HTTPS 异步性能表现
| 评价维度 | 表现 | 备注 |
|---|---|---|
| 原始并发速度 | 🟢 极高 | 与业界最快的 aiohttp 持平或仅慢 5% |
| 易用性 | 🟢 极高 | API 与 requests 高度相似,学习成本极低 |
| 功能丰富度 | 🟢 高 | 原生支持 HTTP/2、SOCKS 代理、超时精细控制 |
| 稳定性 | 🟢 高 | 底层使用成熟的 anyio 和 httpcore,社区活跃,bug 修复快 |
| 极端场景性能 | 🟡 中等 | 对于需要极低延迟的连接复用,aiohttp 或自定义底层可能更优 |
最终建议:
- 如果你需要高性能并发 HTTP 请求,并且希望代码简洁、现代、功能完善,HTTPX 异步版本是最好的选择之一。
- 如果你只是做简单的少量请求(比如一个 API 调用),直接用
requests更方便。 - 如果你正在构建一个要求极致性能、底层高度可控的爬虫或代理服务,可以考虑
aiohttp或基于curio/trio的定制方案,但普通项目完全没必要。