Python异步编程asyncio从入门到生产
一、asyncio适合什么场景
asyncio 适合大量 IO 等待的场景,例如 HTTP 调用、数据库访问、消息队列消费和爬虫。它不是让 CPU 计算自动变快,而是用事件循环在等待网络或磁盘时切换到其他任务。理解这一点很关键:异步提升的是并发等待能力,不是单个任务的计算速度。
| 场景 | 是否适合asyncio | 原因 |
|---|---|---|
| 调用多个外部接口 | 适合 | 等待时间多 |
| WebSocket 长连接 | 适合 | 连接数量多 |
| 图片压缩 | 不适合单独使用 | CPU 密集 |
| 大量 Pandas 计算 | 不适合 | 会阻塞事件循环 |
二、协程、任务与事件循环
async def 定义协程,await 等待可等待对象,asyncio.create_task 把协程提交给事件循环并发执行。下面示例并发请求多个 URL,整体耗时接近最慢的那个请求,而不是所有请求耗时相加。
import asyncio
import httpx
async def fetch(client, url):
resp = await client.get(url, timeout=5)
resp.raise_for_status()
return resp.text
async def main():
urls = ["https://example.com/a", "https://example.com/b"]
async with httpx.AsyncClient() as client:
tasks = [asyncio.create_task(fetch(client, u)) for u in urls]
results = await asyncio.gather(*tasks)
print(len(results))
asyncio.run(main())
不要在协程里调用阻塞函数,例如 time.sleep、同步 requests 或长时间 CPU 循环。它们会卡住整个事件循环。
三、生产代码要控制并发
异步很容易把并发开得过大,导致对方接口被打爆,自己也耗尽连接、文件描述符或内存。生产环境应使用连接池、超时、重试和信号量控制。
import asyncio
import httpx
sem = asyncio.Semaphore(20)
async def safe_fetch(client, url):
async with sem:
for attempt in range(3):
try:
return await client.get(url, timeout=3)
except httpx.TimeoutException:
if attempt == 2:
raise
await asyncio.sleep(0.2 * (attempt + 1))
并发数没有通用答案,要按下游限流、机器资源和接口耗时压测。超时必须设置,否则一次网络抖动可能让任务长期挂起。重试也要克制,最好只对幂等请求启用,并加入退避。
四、异常、取消与观测
asyncio.gather 默认遇到异常会传播,其他任务的状态需要认真处理。服务关闭时,要支持取消任务、释放连接和提交最后的指标。后台任务不能悄悄吞异常,否则线上只会看到“数据少了”,看不到报错。
async def worker(queue):
while True:
item = await queue.get()
try:
await handle(item)
except Exception:
logger.exception("handle failed", extra={"item": item})
finally:
queue.task_done()
实践建议:异步项目从一开始就统一 HTTP 客户端、数据库驱动和日志规范;所有外部调用设置超时;用指标观察队列长度、任务耗时、异常率和并发数。asyncio 的价值在于可控并发,失控的异步只会把问题放大。
部署时还要关注运行模型。FastAPI、aiohttp 等框架通常由 ASGI Server 承载,进程数、事件循环实现和连接池大小要一起配置。CPU 密集任务应放到进程池、任务队列或独立服务中,避免阻塞主事件循环。排障时可以打开慢回调日志,定位哪个协程长期不释放控制权。写异步代码的核心不是到处加 async,而是保证每个等待点都可控、可取消、可观测。
测试异步代码时,应覆盖超时、取消、下游失败和并发竞争。很多问题在单请求下不会出现,只有并发执行时才暴露资源泄露或顺序假设。
📌 本文是《后端工程实战》系列,持续更新,关注不迷路。
👉 下一篇:《Python装饰器从语法糖到生产级应用》,讲如何把超时、重试和日志等横切逻辑稳定复用。
💬 你在实际项目里遇到过 asyncio 任务泄漏、取消失效或事件循环阻塞的问题吗?评论区聊聊。
(觉得有用点个赞+收藏,方便回头查阅)
🔧 相关可运行源码/资料已整理成资源包,可在我主页的资源里自取。
更多推荐


所有评论(0)