选择中间人
celery需要一个单独的服务用于接受和发送消息,这个服务称为’消息中间件’。
假设使用redis作为中间件,
task.py:
from celery import Celery
app = Celery('task', broker='redis://localhost//')
@app.task
def add(x, y):
return x + y
上面的代码实例化了一个Celery实例。为了在别的模块使用该实例作为创建异步任务、管理任务执行者的入口,这个实例必须时可以的引用的。
第一个参数是Celery实例所在的模块名称,这个参数是必需的,用于在__main__模块创建任务时自动生成名称。
这里同时定义了一个名为add的任务。
运行Celery任务执行者服务器
celery -A tasks worker --loglevel=info
调用异步任务
打开另一个终端
>>> from task import add
>>> add.delay(4, 4)
当前任务由刚才运行的任务执行者运行,检查前一个终端即可看到运行过程。
使用delay方法调用函数会返回AsyncResult类型的实例,该实例可用于检查任务状态,等待任务执行结果,或者获取任务的返回结果。如果任务失败,也可以通过这个实例获取异常和异常回溯。
任务结果默认不可用,可以配置backend参数以远程调用或追溯任务:
app = Celery('task', backend='redis://', broker='redis://')
可以使用get方法获取任务结果,并用timeout参数指定等待时间:
res.get(timeout=10)
get方法会抛出任务抛出的异常,通过指定propagate参数为False,可以避免异常。
也可以通过traceback属性获取异常追溯
结果后台会为了存储或传输结果会占用资源,必须对每个AsyncResult实例调用get()或forget()方法以释放资源。
配置
可以直接设置app的配置:
app.conf.task_serializer = 'json'
或
app.conf.update(
task_serializer='json',
accept_content=['json'], # Ignore other content
result_serializer='json',
timezone='Europe/Oslo',
enable_utc=True,
)
也可以时用专门的配置文件:
app.config_from_object('celeryconfig')
配置文件大致如下
broker_url = 'pyamqp://'
result_backend = 'rpc://'
task_serializer = 'json'
result_serializer = 'json'
accept_content = ['json']
timezone = 'Europe/Oslo'
enable_utc = True
在项目中使用celery
项目结构:
proj/__init__.py
/celery.py
/tasks.py
proj/celery.py
from __future__ import absolute_import, unicode_literals
from celery import Celery
app = Celery('proj',
broker='redis://',
backend='redis://',
include=['proj.tasks'])
# Optional configuration, see the application user guide.
app.conf.update(
result_expires=3600,
)
if __name__ == '__main__':
app.start()
celery.py模块创建了一个Celery实例,在项目中引用这个实例即可创建异步任务。
- include 参数指定了任务执行者启动时要引入的模块。引入后任务执行者才能找到任务。
proj/tasks.py
from __future__ import absolute_import, unicode_literals
from .celery import app
@app.task
def add(x, y):
return x + y
@app.task
def mul(x, y):
return x * y
@app.task
def xsum(numbers):
return sum(numbers)
在proj的上层目录使用celery程序即可启动任务执行者:
$ celery -A proj worker -l info
--------------- celery@halcyon.local v4.0 (latentcall)
--- ***** -----
-- ******* ---- [Configuration]
- *** --- * --- . broker: amqp://guest@localhost:5672//
- ** ---------- . app: __main__:0x1012d8590
- ** ---------- . concurrency: 8 (processes)
- ** ---------- . events: OFF (enable -E to monitor this worker)
- ** ----------
- *** --- * --- [Queues]
-- ******* ---- . celery: exchange:celery(direct) binding:celery
--- ***** -----
celery默认以CPU数量启动任务执行者的进程数,可以通过celery worker -c指定任务执行者的数量。如果任务中主要是I/O操作,可以考虑增加worker的数量。否则并不能显著提高执行效率,甚至可能降低性能。
celery也支持多线程。
–app参数指定celery实例,值的格式module.path:attribute
对于–app=proj,celery会按如下顺序搜索相关实例:
- 名为proj.app的参数
- 名为proj.celery的参数
- proj模块中值为Celety应用的参数
- 名为proj.celety.app的参数
- 名为proj.celery.celery的参数
- 模块proj.celery中值为Celery应用的参数
调用任务
delay()方法实际上是apply_async()的缩写。apply_async()允许指定任务的执行时间(countdown)以及任务的调用队列(queue)等:
res = add.apply_async((2, 2), queue='lopri', countdown=10)
# 检查任务状态
res.state # 'FAILURE'
# 获取任务id
res.id # 'd6b3aea2-fb9b-4ebc-8da4-848818db9114'
# 获取AsyncResult实例
from proj.celery import app
res = app.AsyncResult('d6b3aea2-fb9b-4ebc-8da4-848818db9114')
任务常见状态:
PENDING -> STARTED -> SUCCESS
PENDING状态实际上是未知任务的默认状态。
只有设置了task_track_started
参数或在装饰其中指定@task(track_started=True)
才会有STARTED状态。
流式调用
当希望将任务签名传递给另一个进程或作为另一个函数的参数时,可以使用signatures。
sa = add.signature((2, 2), countdown=10)
# 调用
res = sa.delay()
res.get()
groups
groups将一组任务同时调用,并成组返回结果
from celery import group
from proj.tasks import add
group(add.s(i, i) for i in range(10)),get() # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
g = group(add.s(i) for i in range(10))
g(10).get() # [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
chains
任务也可以连接起来依次调用
from celery import chain
from proj.tasks import add, mul
chain(add.s(4, 4) | mul.s(8))().get()
chords
chrods是有回调函数的组
from celery import chord
from proj.tasks import add, xsum
chord((add.s(i, i) for i in range(10)), xsum.s())().get()
当一个组与另一个任务组成链条时,自动转为chord
(group(add.s(i, i) for i in range(10)) | xsum.s())().get()
任务路由
celery支持通过名称分配任务给指定的任务队列
app.conf.update(
task_routes = {
'proj.tasks.add': {'queue': 'hipri'},
},
)
也可以通过在运行任务时指定apply_async参数指定任务队列
from proj.tasks import add
add.apply_async((2, 2), queue='hipri')
再通过命令行参数-Q指定任务执行者执行队列中的任务
celery -A proj worker -Q hipri
使用逗号选择多个队列
celery -A proj worker -Q hipri,celery