在Flask的Celery文档中,为什么Celery任务需要命名?

2 投票
1 回答
2380 浏览
提问于 2025-04-26 12:27

在文档中,@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 个回答

5

在Flask的文档中,任务的name没有被设置,因为代码被认为是在一个tasks模块里面,所以任务的名字会自动生成为tasks.add。在Celery的文档中提到:

每个任务必须有一个唯一的名字,如果没有提供自定义名字,系统会根据函数名自动生成一个新的名字。

想了解更多信息,可以查看Celery文档中的名字部分。

在Github的另一个例子中,作者明确设置了任务的名字,而不是依赖自动生成的名字。如果作为主模块运行,自动生成的名字会是__main__.tasks,这正是运行Flask服务器时的情况。

更新:关于你遇到这个问题的原因:

当你通过传递xy访问/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'

如你所见,这就是你收到这个错误的原因,而不是因为你去掉了任务名字。

撰写回答