Celery 应用:Application用法详解

1、Celery 应用:Application

        Celery 在使用前必须要进行实例化,实例化之后被称为应用程序(简称 app)。 应用程序是线程安全的,因此具有不同配置、组件和任务的多个 Celery 应用程序可以在同一进程空间中共存。 创建一个实例:

from celery import Celery
app = Celery()
app
<Celery __main__ at 0x250bbc65eb8>

最后一行显示的内容包含 app 类的 Celery 名称,当前主模块 (main) 以及对象的内存地址(0x250bbc65eb8)。

2、celery的主名称:Main Name

        其中主模块的名称是比较重要的。

        当在 Celery 中发送任务消息时,该消息不会包含任何源代码,而只包含您要执行的任务的名称。这与主机名在 Internet 上的工作方式类似:每个工作人员都维护一个任务名称与其实际功能的映射,称为任务注册表。

        每当定义任务时,该任务也将添加到本地注册表中:

@app.task
... def add(x, y):
...     return x + y
... 
add
<@task: __main__.add of __main__ at 0x250bbc65eb8>
add.name
'__main__.add'
app.tasks['__main__.add']
<@task: __main__.add of __main__ at 0x250bbc65eb8>

在上面的实例中 __main__再次出现,这是因为 Celery 无法检测到该函数属于哪个模块时,自动会使用主模块的名称作为任务名称的开头。 这只是一组有限实例中的问题:

  • 如果在其中定义任务的模块作为程序运行。

  • 如果应用程序是在python shell(repl)中创建的。

例如,这里的 tasks 模块还用于用 app.worker_main() 启动一个Worker:

tasks.py

from celery import Celery

app = Celery()


@app.task
def add(x, y): return x + y


if __name__ == '__main__':
    app.worker_main()

        当执行此模块时,任务将以“ __main__”开头命名,但是当模块被另一个进程导入时,比如调用任务,任务将以“ tasks”(模块的真实名称)开头命名:

>>> from tasks import add

>>> add.name

tasks.add

也可以设置主模块的名称:

>>> app = Celery('tasks')

>>> app.main

'tasks'

>>> @app.task

... def add(x, y):

... return x + y

>>> add.name

tasks.add

3、celery配置:Configuration

        可以设置几个选项来更改 Celery 的工作方式。这些选项可以直接在app实例上设置,也可以使用专用的配置模块。

配置如下 app.conf

>>> app.conf.timezone
'Europe/London'

也可以直接设置对应的值:

>>> app.conf.enable_utc = True

也可以通过 update 进行一次设置多个键值:

>>> app.conf.update(

... enable_utc=True,

... timezone='Europe/London',

...)

配置对象由多个字典组成,按以下顺序进行查询:

  1. 在运行时所做的更改。

  2. 配置模块(如果有的话)

  3. 默认配置(celery.app.defaults) 也通过 app.add_defaults() 进行配置新的默认配置。

3.1 config_from_object

config_from_object() 可以从配置对象中进行加载配置。

这可以是配置模块,或任何具有配置属性的对象。

注意调用时将重置先前设置的任何配置 config_from_object()。如果你想设置额外的配置,你应该在之后进行。

案例1:使用模块名称

app.config_from_object() 可以从一个 python 模块中(包含属性名称)进行加载,例如:celeryconfigmyproj.config.celerymyproj.config:CeleryConfig

from celery import Celery

app = Celery()

app.config_from_object('celeryconfig')

其中 celeryconfig 模块内容如下:

celeryconfig.py

enable_utc = True

timezone = 'Europe/London'

导入 celeryconfig 程序就可以应用。

案例2:配置模块对象

可以配置模块对象,但不建议这样用。

import celeryconfig

from celery import Celery

app = Celery()

app.config_from_object(celeryconfig)

案例3:使用配置类/对象

from celery import Celery

app = Celery()

class Config:

    enable_utc = True

    timezone = 'Europe/London'

app.config_from_object(Config)

# or using the fully qualified name of the object:

# app.config_from_object('module:Config')

3.2 config_from_envvar

app.config_from_envvar() 可以从环境变量获取信息进行配置, 例如,从名称为 CELERY_CONFIG_MODULE 的环境变量中加载配置:

import os

from celery import Celery

#: Set default configuration module name

os.environ.setdefault('CELERY_CONFIG_MODULE', 'celeryconfig')

app = Celery()

app.config_from_envvar('CELERY_CONFIG_MODULE')

然后,您可以指定要通过环境使用的配置模块:

$ CELERY_CONFIG_MODULE="celeryconfig.prod" celery worker -l info

3.3 celery过滤配置:Censored configuration

        如果您想打印出配置,作为调试信息或类似信息,您可能还想过滤掉密码和 API 密钥等敏感信息。

Celery 附带了几个可用于显示配置的实用程序,其中之一是humanize():

>>> app.conf.humanize(with_defaults=False, censored=True)

        此方法将配置作为表格字符串返回。with_defaults默认情况下,这将仅包含对配置的更改,但您可以通过启用参数来包含内置的默认键和值。

如果您希望将配置作为字典使用,则可以使用以下table()方法:

>>> app.conf.table(with_defaults=False, censored=True)

注意:Celery 无法删除所有敏感信息,因为它仅使用正则表达式来搜索常用命名键。如果您添加包含敏感信息的自定义设置,您应该使用 Celery 识别为秘密的名称来命名密钥。

如果命名中含有子字符串,将会进行过滤:

APITOKENKEYSECRETPASSSIGNATUREDATABASE

4、懒加载:Laziness

程序是懒加载的,在没有实际使用的情况下,是不会进行加载的。 创建一个 Celery 程序的流程如下:

创建用于事件的逻辑操作实例:

  • 创建任务注册表

  • 将自身设置为当前应用程序(如果禁用 set_as_current 参数则不会)

  • 调用 app.on_init() 回调函数(默认情况下不执行任何操作。

app.task() 装饰器不会在定义任务时创建任务,创建任务通常在使用该任务或应用程序完成后进行创建。

该实例会显示在使用任务或访问属性之前,是如何创建任务的:

>>> @app.task

>>> def add(x, y):

... return x + y

​

>>> type(add)

<class 'celery.local.PromiseProxy'>

​

>>> add.__evaluated__()

False

​

>>> add # <-- causes repr(add) to happen

<@task: __main__.add>

​

>>> add.__evaluated__()

True

应用程序的完成要么通过调用显式地发生,要么 通过访问 属性app.finalize()隐式地发生。

app.tasks完成创建对象将:

  • 复制必须在应用之间共享的任务

    默认情况下共享任务,如果装饰器的 shared 参数,该任务为私有任务。

  • 评估所有挂起的任务装饰器

  • 确保所有任务都绑定到当前应用程序

    任务绑带到应用程序,便于从配置中获取配置信息。

4.1 默认应用程序

        Celery 并不总是有应用程序,它曾经只有一个基于模块的 API。在 Celery 5.0 发布之前,旧位置提供了一个兼容性 API,但已被删除。

        Celery 总是创建一个特殊的应用程序——“默认应用程序”,如果没有自定义应用程序被实例化,就会使用它。

celery.task模块不再可用。使用应用程序实例上的方法,而不是基于模块的 API:

from celery.task import Task   # << OLD Task base class.

from celery import Task        # << NEW base class.

5、打破链式操作:Breaking the chain

        虽然可以依赖于当前设置的应用程序,但最佳做法是始终将应用程序实例传递给需要它的任何内容。 通常将这种做法称为 app chain,根据传递的应用程序创建一系列实例。 

以下示例被认为是不好的做法:

from celery import current_app


class Scheduler:


    def run(self):

        app = current_app

应该将 app 作为参数进行传递:

class Scheduler:

    def __init__(self, app):
        self.app = app

Celery 在内部使用该celery.app.app_or_default()函数,因此一切都可以在基于模块的兼容性 API 中工作:

from celery.app import app_or_default

​

class Scheduler(object):

    def __init__(self, app=None):

        self.app = app_or_default(app)

在开发中你可以设置CELERY_TRACE_APP ,如果应用程序链中断,则引发异常的环境变量:

$ CELERY_TRACE_APP=1 celery worker -l info

6、抽象任务:Abstract Tasks

使用 task() 装饰器创建的所有任务都将从应用程序继承 Task 类。 也可以通过 base 参数进行指定基类:

@app.task(base=OtherTask):

def add(x, y):

    return x + y

创建自定义任务类,需要继承 celery.Task

from celery import Task

class DebugTask(Task):

    def __call__(self, *args, **kwargs):
        print('TASK STARTING: {0.name}[{0.request.id}]'.format(self))
        return self.run(*args, **kwargs)

注意:如果重写了任务的__call__方法,那么还需要调用self.run来执行任务的主体,这一点非常重要。不要调用super().__call____call__中性基类的方法celery.Task仅供参考。为了优化,已经展开到celery.app.trace.build_tracer中。Trace_task,如果没有定义__call__方法,则调用直接在自定义任务类上运行。

中性基类是特殊的,因为它还没有绑定到任何特定的应用程序。一旦任务绑定到应用程序,它就会读取配置以设置默认值,等等。

要实现基类,您需要使用app.task() 装饰器创建一个任务:

@app.task(base=DebugTask)

def add(x, y):

    return x + y

也可以通过更改应用程序的 app.task() 属性来更改其默认基类:

>>> from celery import Celery, Task

>>> app = Celery()

>>> class MyBaseTask(Task):
...    queue = 'hipri'

>>> app.Task = MyBaseTask
>>> app.Task
<unbound MyBaseTask>

>>> @app.task
... def add(x, y):
...     return x + y

>>> add
<@task: __main__.add>

>>> add.__class__.mro()
[<class add of <Celery __main__:0x1012b4410>>,
 <unbound MyBaseTask>,
 <unbound Task>,
 <type 'object'>]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值