在Flask的Celery文档中,为什么Celery任务需要命名?
在文档中,@celery.task这个装饰器没有传递参数,但在GitHub的例子里,它被称为"tasks.add"。这是为什么呢?当我去掉这个名字时,例子就不再工作了,报错信息是:
KeyError: '__main__.add'
[1] http://flask.pocoo.org/docs/0.10/patterns/celery/ [2] https://github.com/thrisp/flask-celery-example/blob/master/app.py#L25
1 个回答
在Flask的文档中,任务的name
没有被设置,因为代码被认为是在一个tasks
模块里面,所以任务的名字会自动生成为tasks.add
。在Celery的文档中提到:
每个任务必须有一个唯一的名字,如果没有提供自定义名字,系统会根据函数名自动生成一个新的名字。
想了解更多信息,可以查看Celery文档中的名字部分。
在Github的另一个例子中,作者明确设置了任务的名字,而不是依赖自动生成的名字。如果作为主模块运行,自动生成的名字会是__main__.tasks
,这正是运行Flask服务器时的情况。
更新:关于你遇到这个问题的原因:
当你通过传递x
和y
访问/test
页面时,任务是从hello_world
函数发送的:
res = add.apply_async((x, y))
因为任务add
在__main__
模块里面,所以它的名字会是__main__.add
,并以这个名字发送给工作者。但另一方面,你启动的工作者:
celery worker -A app.celery
把这个任务注册为app.add
,所以你会收到这个错误:
[2014-10-10 10:32:29,540: ERROR/MainProcess] Received unregistered task of type '__main__.add'.
The message has been ignored and discarded.
Did you remember to import the module containing this task?
Or maybe you are using relative imports?
Please see http://docs.celeryq.org/en/latest/userguide/tasks.html#task-names for more information.
The full contents of the message body was:
{'timelimit': (None, None), 'utc': True, 'chord': None, 'args': (2787476, 36096995), 'retries': 0, 'expires': None, 'task': '__main__.add', 'callbacks': None, 'errbacks': None, 'taskset': None, 'kwargs': {}, 'eta': None, 'id': '804e10a0-2569-4338-a5e3-f9e07689d1d1'} (218b)
Traceback (most recent call last):
File "/home/peter/env/celery/lib/python2.7/site-packages/celery/worker/consumer.py", line 455, in on_task_received
strategies[name](message, body,
KeyError: '__main__.add'
查看工作者的输出:
[tasks]
. app.add
. celery.backend_cleanup
. celery.chain
. celery.chord
. celery.chord_unlock
. celery.chunks
. celery.group
. celery.map
. celery.starmap
Celery只会把任务名字发送给工作者来执行,所以当你明确设置任务名字时,hello_world
函数会用这个名字发送任务,而这个名字在工作者中是注册过的。
更新:
任务名字可以是你想要的任何名字,可以简单地叫add
,而且你的celery任务不一定要放在tasks
模块里。想更深入了解任务名字,可以尝试这个:
去掉明确的任务名字,然后启动一个工作者:
celery worker -A app.celery
在另一个终端窗口中,cd
到代码目录,启动一个交互式的Python环境,试试这个:
>>> import app
>>> app
<module 'app' from 'app.pyc'>
>>> app.add
<@task: app.add of app:0xb6a29a6c>
>>> # check the name of the task
... app.add.name
'app.add'
>>> t = app.add.delay(2, 3)
>>> t.result
5
如你所见,我们没有使用明确的名字,但它照样按预期工作,因为我们发送任务的地方的名字和工作者中注册的名字是一样的(见上文)。
现在回到你为什么在去掉任务名字时会出现这个错误,任务是从app.py
发送的,对吧?在同一目录下运行这个:
$ python -i app.py
然后用Ctrl
+ C
中断Flask服务器,试试这个:
>>> add.name
'__main__.add'
如你所见,这就是你收到这个错误的原因,而不是因为你去掉了任务名字。