在使用 Flower 的时候,可能你也发现了,Flower 支持一种可以作为 Celery 子命令执行,可能你惊讶过,但是,它是如何实现的,你了解么?本文将向你 Flower 是如何实现这个功能的。

以前在使用 Flower 的时候,我也惊奇居然还有这种玩法,当初搜了一圈,发现只有 Celery 自己的文档有做介绍,虽然只做了一点点介绍,但是,至少是可操作的。官方的文档在Extensions and Bootsteps

最近因为要为 CeleryBeatRedis 写一个CRUD 任务的Admin,希望以类似 Flower 的方式实现,所以重新回顾了这个内容,顺便总结一番给大家分享一下,顺带一提,刚才的文档有两块内容,一块就是本文要讲的 扩展,另一块就是以后也会总结的Bootsteps 了。

可能以下较多内容都会引用官方文档,但会加以自身理解进行丰富简化。

给 Celery 添加子命令

要给 Celery 添加新的子命令,需要使用到的工具是 setuptoolsentry-pointsEntry-pointssetup.py 文件中的一个特别的元数据,当你安装完成之后,你可以使用 pkg 资源模块读取到它。

Celery 能够识别被安装为 celery.commands 的 entry-points,但是,这个值必须指向 celery.bin.base.Command 的合法子类,否则,Celery 会哭给你看。

官方文档并没有太多关于继承的资料,但是,根据我读源码的经验,事实上,你最小只需要实现 run_from_argv 即可。函数原型为:

  1. def run_from_argv(self, prog_name, argv, command=None)

因为源码调用是这样的:

  1. return cls(
  2. app=self.app, on_error=self.on_error,
  3. no_color=self.no_color, quiet=self.quiet,
  4. on_usage_error=partial(self.on_usage_error, command=command),
  5. ).run_from_argv(self.prog_name, argv[1:], command=argv[0])

所以,你应该以此为入口开始编写代码逻辑。

下面是一个注册 celery ceexample 命令的 setup.py 精简示例文件,虽然如此精简,但是完全可以保证你可以验证这个机制。

  1. from setuptools import setup, find_packages
  2. setup(
  3. name='ceexample',
  4. packages=find_packages(exclude=['contrib', 'docs', 'tests']),
  5. entry_points={
  6. 'celery.commands': [
  7. 'ceexample = ceexample.command:ExampleCommand',
  8. ],
  9. },
  10. )

entry_points 的定义就是一个字典,然后 key 应该是 celery.commands, 值的话是个字符串数组,里面是一个个等式

然后是编写代码,整个的目录结构是这样的:

  1. .
  2. ├── ceexample
  3. ├── __init__.py
  4. └── command.py
  5. └── setup.py

作为必须的 `_init_.py文件中是没有内容的,然后command.py` 文件中的内容也是异常简单:

  1. from celery.bin.base import Command
  2. class ExampleCommand(Command):
  3. def run_from_argv(self, prog_name, argv=None, command=None):
  4. print "run_from_argv called...."

至此,整个代码就算是编写完毕了,这个时候可以安装下试试,只需要使用一下安装命令:

  1. python setup.py install

然后你可以直接先敲一个 celery 命令试一下,你会发现是输出一序列的说明,拉到最后面,你会发现这样的东西:

图 1:Celery 扩展示例

说明我们的扩展是注册成功啦,然后尝试运行一下:

  1. celery ceexample

如果不出意外的话,控制台打印出来的应该是:

  1. run_from_argv called....

Celery 扩展就讲到这,内容也差不多就这么多了。

扩展知识

  1. entry_points 中 celery.commands 在安装后发生了什么?

    这个问题稍微有点复杂,我在另外一篇关于 setup.py 的文章有一些介绍,如果感兴趣可以参考一下。

  2. 除了实现 run_from_argv 外还只实现其他方法么?

    事实上,Celery 中的 Command 实现了大部分方法的代码,所以,你完全可以实现其他方法,但是,官方文档是不会跟你介绍有哪些方法的,所以只能你自己去看代码去找。

    需要说明的是,最后的最后,run 方法是你需要重写的,因为如果你什么都不写得话,将会在这个方法报错。

  3. 在扩展里面如何拿到 Celery 的数据

    因为扩展的类是继承自 celery.bin.base.Command 的,所以我们完全可以无条件得拿到 Command 类中的数据,这当然包括 Celery 的实例对象 self.app

    拿到这个对象后,我们可以获取的数据就非常多了,例如消息队列,结果队列的地址,等等。。