requests 和 aiohttp 都是 Python 中用于发起 HTTP 请求的流行库,但它们的设计哲学和适用场景截然不同。选择哪一个,取决于你的项目是否使用 异步(async/await)架构。
🚦 一句话结论
| 项目类型 | 推荐库 |
|---|---|
| 同步代码(Flask、Django、脚本) | ✅ requests |
| 异步代码(FastAPI、Starlette、Tornado、aiohttp 服务端) | ✅ aiohttp |
💡 关键区别:
requests是 阻塞式(同步) 的 —— 发起请求会“卡住”当前线程直到响应返回。aiohttp是 非阻塞式(异步) 的 —— 发起请求后可立即去做别的事,等响应回来再处理。
🔍 一、详细对比
| 特性 | requests | aiohttp |
|---|---|---|
| 类型 | 同步(Blocking) | 异步(Non-blocking, asyncio) |
| 安装 | pip install requests | pip install aiohttp |
| 简单 GET | requests.get(url) | await session.get(url) |
| 性能(高并发) | ❌ 差(每个请求占一个线程) | ✅ 极好(单线程处理 thousands 并发) |
| 内存占用 | 高(线程开销) | 低(事件循环) |
| 学习曲线 | ⭐ 极低(像写伪代码) | 中(需理解 async/await) |
| 客户端 + 服务端 | 仅客户端 | ✅ 客户端 + 服务端(可写 Web API) |
| WebSocket 支持 | ❌(需其他库) | ✅ 原生支持 |
| HTTP/2 支持 | ❌ | ⚠️ 需额外库(如 aiohttp-http2) |
| 典型用户 | 脚本、爬虫、Django 后台任务 | FastAPI 微服务、实时应用、高并发网关 |
🧪 二、代码示例对比
场景:获取 3 个 URL 的内容
✅ requests(同步,顺序执行)
import requests
import time
urls = ["https://httpbin.org/delay/1"] * 3
start = time.time()
for url in urls:
resp = requests.get(url)
print(resp.status_code)
end = time.time()
print(f"耗时: {end - start:.2f} 秒") # ≈ 3.0 秒(串行)✅ aiohttp(异步,并发执行)
import aiohttp
import asyncio
import time
async def fetch(session, url):
async with session.get(url) as resp:
return resp.status
async def main():
urls = ["https://httpbin.org/delay/1"] * 3
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
results = await asyncio.gather(*tasks)
for r in results:
print(r)
start = time.time()
asyncio.run(main())
end = time.time()
print(f"耗时: {end - start:.2f} 秒") # ≈ 1.1 秒(并发)🔥 结果:同样 3 个延迟 1 秒的请求,
requests花 3 秒(一个接一个)aiohttp花 1 秒(同时发起)
⚠️ 三、重要注意事项
1. 不要在异步函数里用 requests!
# ❌ 危险!会阻塞整个事件循环
@app.get("/bad")
async def bad_endpoint():
data = requests.get("https://api.example.com") # ← 阻塞!其他请求卡住
return data.json()✅ 正确做法:
# ✅ 使用 aiohttp
@app.get("/good")
async def good_endpoint():
async with aiohttp.ClientSession() as session:
async with session.get("https://api.example.com") as resp:
data = await resp.json()
return data如果必须用
requests(如调用不支持异步的 SDK),请用loop.run_in_executor包装:from functools import partial data = await loop.run_in_executor(None, partial(requests.get, url))
2. aiohttp 必须复用 ClientSession
# ❌ 错误:每次新建 session(浪费资源)
async def bad_fetch(url):
async with aiohttp.ClientSession() as session: # ← 每次都新建
async with session.get(url) as resp:
return await resp.text()
# ✅ 正确:全局或长期复用 session
session = aiohttp.ClientSession()
async def good_fetch(url):
async with session.get(url) as resp:
return await resp.text()
# 应用关闭时记得关闭 session
@app.on_event("shutdown")
async def shutdown():
await session.close()📌 最佳实践:在 FastAPI 中通过依赖项管理 session 生命周期。
3. 功能差异
| 功能 | requests | aiohttp |
|---|---|---|
| 自动解压 gzip | ✅ | ✅(默认开启) |
| Session Cookie 持久化 | ✅ | ✅(通过 CookieJar) |
| 文件上传 | files={'file': open(...)} | data=aiohttp.FormData() |
| 超时控制 | timeout=5 | timeout=aiohttp.ClientTimeout(total=5) |
| 重试机制 | 需 urllib3 或第三方 | 需手动实现或用 tenacity |
🛠 四、如何选择?
选 requests 如果:
- 你写的是 脚本、爬虫、Django 后台任务
- 项目是 纯同步(无
async/await) - 追求 最简代码(一行搞定)
选 aiohttp 如果:
- 你在用 FastAPI / Starlette / Sanic
- 需要 高并发调用外部 API(如微服务通信)
- 项目已基于 asyncio
- 需要 WebSocket 客户端
🧩 五、FastAPI 中的最佳实践(使用 aiohttp)
# deps.py
from aiohttp import ClientSession
from fastapi import Depends
async def get_http_client():
# 实际项目中建议用 lifespan 管理全局 session
async with ClientSession() as session:
yield session
# main.py
@app.get("/proxy")
async def proxy_data(http_client: ClientSession = Depends(get_http_client)):
async with http_client.get("https://api.external.com/data") as resp:
return await resp.json()更高级:使用 lifespan 管理全局 session(避免每次请求新建):
from contextlib import asynccontextmanager session: Optional[ClientSession] = None @asynccontextmanager async def lifespan(app: FastAPI): global session session = ClientSession() yield await session.close() app = FastAPI(lifespan=lifespan)
📊 六、性能对比(理论)
| 并发数 | requests(线程池) | aiohttp(单线程) |
|---|---|---|
| 10 | 快 | 快 |
| 100 | 内存飙升,上下文切换开销大 | 依然流畅 |
| 1000+ | 可能崩溃 | ✅ 轻松应对 |
🌐 在 I/O 密集型场景(如 API 网关、聚合服务),aiohttp 性能优势巨大。
✅ 总结
requests | aiohttp | |
|---|---|---|
| 定位 | 同步 HTTP 客户端 | 异步 HTTP 客户端 + 服务端 |
| 哲学 | “简单即美” | “高并发、高性能” |
| 生态 | 通用、无处不在 | 异步 Python 生态核心 |
| 你的项目 | Django / 脚本 → 选它 | FastAPI / 异步 → 必选它 |
如果你正在开发 FastAPI + Tortoise-ORM 项目(典型的异步栈),请坚定使用 aiohttp 作为 HTTP 客户端。而 requests 留给你的同步脚本或数据迁移工具。
评论已关闭