Our Project
当前项目的文件结构:
[root@VM_0_2_centos test002]# tree
.
|-- app.py
|-- celeryconfig.py
0 directories, 2 files
下面对每一个文件进行详细地说明。
celery.py
from __future__ import absolute_import, unicode_literals
from celery import Celery
# app = Celery('proj',
# broker='redis://127.0.0.1:6379',
# backend='redis://127.0.0.1:6379/0',
# include=['proj.tasks'])
app = Celery('proj', include = ['proj.tasks'])
app.config_from_object('proj.celeryconfig')
# Optional configuration, see the application user guide.
app.conf.update(
result_expires=3600,
)
if __name__ == '__main__':
app.start()
celeryconfig.py
# backend and broker
BROKER_URL = 'redis://127.0.0.1:6379'
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/0'
app.py
是celery
应用的切入口,celeryconfig.py
中存放着celery
的相关配置。
配置文件中: BROKER_URL
对应消息中间件,RESULT_BACKEND
存储任务的执行结果。
接着,定义tasks.py
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)
Starting the worker
启动worker
[root@VM_0_2_centos test002]# celery -A app worker -l info
/usr/lib64/python2.7/site-packages/celery/platforms.py:801: RuntimeWarning: You're running the worker with superuser privileges: this is
absolutely not recommended!
Please specify a different user using the --uid option.
User information: uid=0 euid=0 gid=0 egid=0
uid=uid, euid=euid, gid=gid, egid=egid,
-------------- celery@VM_0_2_centos v4.4.2 (cliffs)
--- ***** -----
-- ******* ---- Linux-3.10.0-693.el7.x86_64-x86_64-with-centos-7.7.1908-Core 2020-04-02 16:19:44
- *** --- * ---
- ** ---------- [config]
- ** ---------- .> app: tasks:0x7f18c8eda110
- ** ---------- .> transport: redis://127.0.0.1:6379//
- ** ---------- .> results: redis://127.0.0.1:6379/0
- *** --- * --- .> concurrency: 1 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
[2020-04-02 16:19:44,958: INFO/MainProcess] Connected to redis://127.0.0.1:6379//
[2020-04-02 16:19:44,987: INFO/MainProcess] mingle: searching for neighbors
[2020-04-02 16:19:47,027: INFO/MainProcess] mingle: all alone
[2020-04-02 16:19:47,038: INFO/MainProcess] celery@VM_0_2_centos ready.
这里添加,官方文档中的补充说明。
-
broker
: 可以在配置文件中指定URL
,同样也可以使用-b
选项在命令行中指定其他的代理。 -
concurrency
: 同时处理任务的prefork
的数目[1]。当所有prefork
都处于执行任务阶段,新任务将等待某个任务完成,才能被处理。 -
events
:events
用于触发Celery发送监视消息以监视工作程序中发生的操作。 -
queues
: 队列是worker
使用任务的队列列表。
Stopping the worker
-
使用
ctrl + C
-
pkill -9 -f 'celery worker'
-
ps auxww | awk '/celery worker/ {print $2}' | xargs kill -9
官方文档 http://docs.celeryproject.org/en/master/userguide/workers.html#stopping-the-worker
In the background
后台运行的方法: 这一步骤先跳过
官方文档 http://docs.celeryproject.org/en/master/userguide/daemonizing.html#daemonizing
Calling Tasks
调用tasks.py
中的add
方法:
调用deplay()方法
>>> from proj.tasks import add
>>> res = add.delay(2, 2)
>>> res.get()
4
调用apply_async()方法
apply_async
方法,deplay
方法实际是apply_async
方法的简化版本。apply_async
可以支持多个参数。
>>> res = add.apply_async((2, 2))
>>> res.get()
4
apply_async
可以指定执行选项,例如原型时间(倒数),应当发送的队列等等。
add.apply_async((2, 2), queue = 'lopri', countdown = 10)
>>> res = add.apply_async((2, 2), queue = 'lopri', countdown = 0.5)
>>> res.get()
此示例中,该任务将被发送到lopri
的队列中,并且此任务最早将在发送消息10秒后执行[4]。
等待很长时间,没有得到返回值。
直接调用方法
>>> add(2, 2)
4
直接调用方法,不会触发消息的发送worker
。
官方文档call方法: http://docs.celeryproject.org/en/master/userguide/calling.html#guide-calling
查看任务 id
>>> res = add.delay(2, 2)
>>> res.id
'b022ce12-08e3-442b-8cf9-05d77e12c412
查看任务执行情况
>>> res.failed()
False
>>> res.successful()
True
查看任务执行状态
>>> res.state
'SUCCESS'
任务状态的演进
PENDING -> STARTED -> SUCCESS
pending状态
pending
状态,是未知任务ID的默认状态
>>> from proj.celery import app
>>> res = app.AsyncResult('this-id-does-not-exist')
>>> res.state
u'PENDING'
重复启动状态
PENDING -> STARTED -> RETRY -> STARTED -> RETRY -> STARTED -> SUCCESS
Canvas: Designing Work-flows
有时需要将任务调用的签名传递给另一个进程,或者作为另一个函数的参数。Celery为此使用了一种称为签名的函数。
签名将包装单个任务调用的参数和执行选项,以便可以将其传递给函数,甚至进行序列化并通过网络发送。
可以使用参数(2, 2)
和10秒的倒计时为添加任务创建签名,如下所示:
>>> from proj.tasks import add
>>> add.signature((2, 2))
proj.tasks.add(2, 2)
>>> add.s(2, 2)
proj.tasks.add(2, 2)
这里没有看懂是什么意思?
And there’s that calling API again …
签名实例支持调用API, 意味着具有delay和apply_async方法,区别于签名已经指定了参数签名,add任务接受两个参数,因此指定两个参数的签名将构成完整的签名:
>>> s1 = add.signature((2, 2))
>>> res = s1.delay()
>>> res.get()
4
可以创建不完整的签名来创建partials
的部分。
s2是部分签名,需要另一个参数来完成,可以在调用签名时解决。
>>> s2 = add.s(2) # incomplete partial: add(?, 2)
>>> res = s2.delay(8) # resolves the partial: add(8, 2)
>>> res.get()
10
直观感受,可以利用signature
先补充一个参数,然后通过delay
在补充另一个参数。
官方解释,在这里在现有的参数2
前添加了参数8
, 形成了add(8, 2)
的完整签名。
参考文档
1. 官方文档 http://docs.celeryproject.org/en/master/getting-started/next-steps.html#next-steps
问题汇总
-
同时处理任务的
prefork
的数目.prefork
数目对应什么?答: prefork数目,对应该计算机(包括核心)上的CPU数目。
-
events
是什么?具体如何实践? -
queues
是什么?具体如何实践? -
add.apply_async((2, 2), queue = 'lopri', countdown = 10)
, 此队列lopri
是任意指定的吗? -
Canvas: Designing Work-flows 模块是干什么用的?