木偶人战争模拟器手机版
60.35MB · 2025-10-29
在使用 Celery 构建分布式任务系统时,我们常常会发现:无论是启动 worker、beat,还是 shell,大家都用同一个 app 实例(通常是 Celery() 对象)。那么,为什么 app 实例能在不同进程间“传递”?它的实现手法是什么? 本文将结合源码和代码示例,详细解析 Celery 的设计原理。
Celery 的核心入口通常是这样:
# proj/celery.py
from celery import Celery
app = Celery('proj')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
启动 worker 或 beat 时,命令如下:
celery -A proj worker
celery -A proj beat
这里的 -A proj,就是告诉 Celery 去导入 proj.celery,获取 app 实例。但 worker、beat 是不同进程,为什么都能用同一个 app?
Celery 并不是直接把 Python 对象跨进程传递(这在 Python 里很难做到),而是通过模块导入和配置同步来实现“同一个 app 实例”。
proj.celery 这个模块。app = Celery('proj') 这行代码每次都会创建一个新的 app 实例,但它们的配置和任务注册是完全一致的。# proj/celery.py
from celery import Celery
app = Celery('proj')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
无论 worker 还是 beat,都通过 from proj.celery import app 获得 app 实例。
Celery 的配置(broker、backend、任务列表等)是可序列化的,可以通过环境变量、配置文件、命令行参数等方式传递。
app.config_from_object('django.conf:settings', namespace='CELERY')
这行代码让所有 app 实例都从同一个配置源加载参数,保证一致性。
所有任务都通过 @app.task 装饰器注册到 app 实例。不同进程通过导入同一个 app 实例,自动获得所有任务的注册信息。
# proj/tasks.py
from proj.celery import app
@app.task
def add(x, y):
return x + y
worker、beat 都能通过 app 实例访问到 add 任务。
Python 的对象不能直接跨进程传递(除非用 IPC/序列化),Celery 采用模块导入和配置同步,而不是对象共享。
Celery 的入口脚本(如 worker、beat)会解析 -A 参数,自动导入指定模块,查找 app 实例:
# celery/bin/celery.py
def find_app(app_name):
# 解析 -A 参数,导入模块
module = import_module(app_name)
return getattr(module, 'app')
每个进程都独立导入模块,获得 app 实例。
假如你有如下 Django 项目结构:
proj/
celery.py
tasks.py
manage.py
celery.py 内容:
from celery import Celery
app = Celery('proj')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
tasks.py 内容:
from proj.celery import app
@app.task
def hello(name):
print(f"Hello, {name}!")
启动 worker:
celery -A proj worker
启动 beat:
celery -A proj beat
无论哪个进程,都会导入 proj.celery,获得同样的 app 实例和任务列表。
celery.py),并通过 -A 参数统一导入。@app.task 注册,保证 worker、beat 等进程都能访问。Celery 的设计让分布式任务系统既高效又易于扩展,理解 app 实例的“传递”机制,是高质量项目开发的基础。