Celery Worker 默认是同步进程,不运行事件循环,因此直接在@shared_task中await会报错或失效;正确做法是在任务内用asyncio.run()启动新事件循环。

不能直接把 asyncio 协程塞进 Celery 任务里运行 —— Celery Worker 默认是同步进程,不跑事件循环,await 会报错或静默失效。
为什么 Celery 里直接 await 会出问题
Celery Worker 启动的是普通 Python 进程(或线程),不是 asyncio 事件循环环境。你在 @shared_task 函数里写 await asyncio.sleep(1),会触发 RuntimeWarning: coroutine 'sleep' was never awaited,或者直接抛 TypeError: object Task can't be used in 'await' expression —— 因为 asyncio.run() 没被调用,也没有当前事件循环。
常见错误现象:
- 任务日志里看不到
await后的代码执行痕迹 - 返回值是
而非实际结果 - Worker 进程卡住、CPU 占用低但任务不推进
asyncio 任务必须显式进入事件循环
要在 Celery 中安全运行异步逻辑,得手动启动并管理事件循环。最稳妥的方式是在任务函数内部用 asyncio.run() 包一层,而不是依赖外部环境:
from celery import shared_task import asyncio <p>@shared_task def fetch_url_async(url: str) -> dict:</p><h1>✅ 正确:在任务内启动新事件循环</h1><pre class="brush:php;toolbar:false;">return asyncio.run(_fetch_inner(url))
async def _fetch_inner(url: str) -> dict: import aiohttp async with aiohttp.ClientSession() as session: async with session.get(url) as resp: return {"status": resp.status, "url": url}
注意点:
-
asyncio.run()每次都新建并关闭事件循环,开销可控,适合短生命周期任务 - 不要在任务里复用全局
asyncio.get_event_loop()—— Celery 多 worker 场景下可能引发RuntimeError: There is no current event loop - 若需长连接(如复用
aiohttp.ClientSession),必须在_fetch_inner内创建,不能跨asyncio.run()边界传递
Celery + asyncio 的性能瓶颈与绕过方案
单个 Celery Worker 进程默认只处理一个任务(同步模式),即使你用了 asyncio.run(),也无法并发执行多个异步任务 —— 它只是把“一个任务变快”,没解决“多个任务并行”问题。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
真正提升吞吐的方法是:
- 用
-c参数启动多进程 Worker:celery -A proj worker -c 4,每个进程可独立跑asyncio.run() - 对单个任务内部做并发:比如一个任务要抓 10 个 URL,别写 10 个
@shared_task,改用asyncio.gather(*[fetch_one(u) for u in urls])在一个任务里并发完成 - 避免混用:不要在一个
@shared_task里既调requests.get()(阻塞)又调await aiohttp.get()(异步)—— I/O 模型冲突会导致阻塞整个协程
如果你的任务本质是高并发 I/O 密集型(如爬虫、API 聚合),更推荐跳过 Celery,直接用纯 asyncio.Queue + aiohttp 构建独立服务 —— Celery 的序列化、Broker 中转、结果回写反而成了累赘。
何时该放弃 Celery,改用原生 asyncio.Queue
当你的场景满足以下任意一条,就该考虑不用 Celery:
- 所有任务都在同一台机器、同一个 Python 进程内调度(无跨节点需求)
- 任务生命周期短(
- 需要精细控制并发数(如每秒最多 5 个请求),而 Celery 的
concurrency是进程级粗粒度控制 - 你已经在用 FastAPI/aiohttp,并且希望日志、监控、trace ID 全链路贯通 —— Celery 的中间 broker 会切断上下文
这时候,asyncio.Queue 不是“简化版 Celery”,而是更贴合异步语义的原生工具:它不序列化、不网络传输、无 Broker 依赖,put()/get() 就是协程间通信的自然表达。唯一要注意的是,它无法跨进程,也做不到 Celery 那种任务持久化和重试保障 —— 优势和限制是一体两面。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










