一、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 任务泄漏、取消失效或事件循环阻塞的问题吗?评论区聊聊。
(觉得有用点个赞+收藏,方便回头查阅)
🔧 相关可运行源码/资料已整理成资源包,可在我主页的资源里自取。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐