Celery 定义和调用异步任务Task

https://docs.celeryq.dev/en/stable/userguide/tasks.html

使用app.task装饰器定义

需要通过导入celery app,然后使用@app.task装饰器来定义任务

from celery_app.py import app

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

可以为task任务设置一些额外的参数,如:

from celery_app.py import app

# 单独设置task的序列化器,覆盖全局设置中的序列化器设置。
@app.task(serializer='json')
def add(x, y):
    return x + y

注意事项:如果你的函数、方法有多个装饰器,则必须保证@app.task装饰器是最后一个执行的,如下所示:

from celery_app.py import app

@app.task(serializer='json')
@decorator2
@decorator1
def add(x, y):
    return x + y

使用shared_task装饰器定义

当我们使用@app.task装饰器定义我们的异步任务时,那么这个任务依赖于根据项目名生成的Celery实例(celery配置文件中定义)。

然而我们在进行Django开发时为了保证每个app的可重用性,一般会在每个app文件夹下编写异步任务tasks.py,这些任务并不依赖于具体的Django项目名。使用@shared_task装饰器能让我们避免对某个项目名对应Celery实例的依赖,使app的可移植性更强。

@shared_task装饰器允许您在没有任何具体celery app实例的情况下创建任务:

from celery import shared_task

@shared_task
def add(x, y):
    return x + y

常用的任务参数

设置任务Task名称

每个任务都必须有一个唯一的名称。

如果没有提供显式名称,任务装饰器将为您生成一个名称,该名称将基于 :定义任务的模块py文件名 + 任务函数的名

最佳实践:使用模块名称作为任务名,这样,如果另一个模块中已经定义了具有该名称的任务,则名称不会发生冲突。

@app.task(name='tasks.add')
def add(x, y):
    return x + y

您可以通过调用其 .name 属性来得知任务的名称:

add.name
'tasks.add'

任务参数bind的作用

绑定任务意味着任务的第一个参数将始终是任务实例 ( self ),就像 Python 方法中的self参数一样:

logger = get_task_logger(__name__)

@app.task(bind=True)
def add(self, x, y):
    # self就是任务自身实例
    # 打印任务实例的ID
    logger.info(self.request.id)

任务失败重试(使用 app.Task.retry() )、访问有关当前任务请求的信息(如self.request.id)以及添加到自定义任务基类的任何其他功能都需要bind参数。

ignore_result

如果您不关心任务的结果,请务必设置 ignore_result 选项,因为存储结果会浪费时间和资源。

@app.task(ignore_result=True)
def mytask():
    something()

甚至可以再celery配置文件中使用 task_ignore_result 设置项进行全局设置禁用结果存储。

在调用 apply_async 时,通过传递 ignore_result 布尔参数,可以在每次执行的基础上启用/禁用结果。

result = mytask.apply_async(1, 2, ignore_result=False)

获取请求任务信息

https://docs.celeryq.dev/en/stable/userguide/tasks.html#task-request

app.Task.request 包含与当前正在执行的任务相关的信息和状态。

任务重试retry

app.Task.retry() 可用于重新执行任务,例如在发生可恢复错误时。

当您调用 retry 时,它会使用相同的任务 ID 发送一条新消息,并且会注意确保该消息与原始任务传递到同一队列。

当重试任务时,这也会记录为任务状态,以便您可以使用结果实例跟踪任务的进度(请参阅状态)。

retry函数参数:https://docs.celeryq.dev/en/stable/reference/celery.app.task.html#celery.app.task.Task.retry

task有关重试的常用参数:

  • max_retries,最大重试次数,默认为3
  • retry_backoff:True或者False、或者一个数值,用于设置异常自动重试的时间间隔。
    • 如果是True,则第一次是1秒,第二次是2秒,第三次是4秒的指数形式递增。
    • 如果是数值,则延迟时间为: 第n次:(retry_backoff×2^(n-1)) 秒。

示例:

# retry_backoff:异常自动重试的时间间隔 第n次(retry_backoff×2^(n-1))s
# max_retries:异常自动重试次数的上限
@app.task(bind=True, max_retries=3)
def send_twitter_status(self, oauth, tweet):
    try:
        twitter = Twitter(oauth)
        twitter.update_status(tweet)
    except (Twitter.FailWhaleError, Twitter.LoginError) as exc:
        # retry函数也可以使用max_retries,会覆盖task设置的max_retries。
        # 抛出retry函数异常就是等同于执行重试操作
        raise self.retry(exc=exc, max_retries=5)

再来一个以前学习的实例(发送短信验证码)

@shared_task(bind=True)
def send_sms(self, mobile, smscode):
    send_result = -1
    try:
        send_result = CCP().send_template_sms(to=mobile, datas=[smscode, "%d" % (SMS_EXPIRE / 60)], temp_id=1)
    except Exception as exc:
        raise self.retry(exc=exc, max_retries=3)
    finally:
        # 如果返回结果为-1,则表示发送失败
        if send_result == -1:
            # 尝试再次发送
            raise self.retry(exc=Exception('短信供应商发送失败,重试中..'), max_retries=3)

注意:任务发生异常重试前,默认是等待设置中的default_retry_delay的值(默认3分钟)

可以全局设置default_retry_delay,也可以单独给task设置。

甚至可以在调用retry的时候提供countdown参数来覆盖default_retry_delay

@app.task(bind=True, default_retry_delay=30 * 60)  # retry in 30 minutes.
def add(self, x, y):
    try:
        something_raising()
    except Exception as exc:
        # overrides the default delay to retry after 1 minute
        raise self.retry(exc=exc, countdown=60)

任务异常自动重试

https://docs.celeryq.dev/en/stable/userguide/tasks.html#automatic-retry-for-known-exceptions

该功能在4.0版本后提供。

有时候我们想在某些特定异常时自动进行任务重试。

在4.0版本后,可以在task装饰器中使用autoretry_for 参数告诉 Celery 进行自动重试任务:

from twitter.exceptions import FailWhaleError

# 当refresh_timeline任务发生FailWhaleError异常时会自动进行任务重试
@app.task(autoretry_for=(FailWhaleError,))
def refresh_timeline(user):
    return twitter.refresh_timeline(user)

如果要为内部 retry() 调用指定自定义参数,请将 retry_kwargs 参数传递给 app.task() 装饰器:

# 使用retry_kwargs参数指定retry函数的参数,以此覆盖task中的max_retries设置的3次重试次数
@app.task(max_retries=3, autoretry_for=(FailWhaleError,),
          retry_kwargs={'max_retries': 5})
def refresh_timeline(user):
    return twitter.refresh_timeline(user)

使用autoretry_for参数相当于手动处理异常:

@app.task
def refresh_timeline(user):
    try:
        twitter.refresh_timeline(user)
    except FailWhaleError as exc:
        raise refresh_timeline.retry(exc=exc, max_retries=5)

调用异步任务

https://docs.celeryq.dev/en/stable/userguide/calling.html#guide-calling

Celery提供了2种以异步方式调用任务的方法,delayapply_async方法

使用 delay() 方法调用任务:

# 导入任务函数
from proj.tasks import add

# 调用任务函数的delay()方法,delay方法可以传递任务的参数
add.delay(2, 2)

delay方法实际上是另一个名为 apply_async() 的方法的快捷方式:

apply_async()方法可以使用更多的参数:

格式:apply_async(args=(), kwargs={}, route_name=None, queue=指定队列, **options,)

# 导入任务函数
from proj.tasks import add

# 调用任务函数的delay()方法,apply_async传递参数时使用args来指定。
add.apply_async(args=[3, 5])

每个任务调用都会被赋予一个唯一标识符(UUID)——这就是任务 ID。

delayapply_async 方法返回一个 AsyncResult 实例,该实例可用于跟踪任务执行状态。

但为此,您需要启用结果后端(result_backend),以便可以将状态存储在某处。

避免启动同步子任务

https://docs.celeryq.dev/en/stable/userguide/tasks.html?spm=wolai.workspace.0.0.62b744afi4bqVA#avoid-launching-synchronous-subtasks

错误做法:

@app.task
def update_page_info(url):
    page = fetch_page.delay(url).get()
    info = parse_page.delay(page).get()
    store_page_info.delay(url, info)

@app.task
def fetch_page(url):
    return myhttplib.get(url)

@app.task
def parse_page(page):
    return myparser.parse_document(page)

@app.task
def store_page_info(url, info):
    return PageInfo.objects.create(url, info)

正确做法:

def update_page_info(url):
    # fetch_page -> parse_page -> store_page
    chain = fetch_page.s(url) | parse_page.s() | store_page_info.s(url)
    chain()

@app.task()
def fetch_page(url):
    return myhttplib.get(url)

@app.task()
def parse_page(page):
    return myparser.parse_document(page)

@app.task(ignore_result=True)
def store_page_info(info, url):
    PageInfo.objects.create(url=url, info=info)

自定义异常类celery中的注意事项:

https://docs.celeryq.dev/en/stable/userguide/tasks.html#creating-pickleable-exceptions

获取任务执行结果

https://docs.celeryq.dev/en/stable/reference/celery.result.html

posted @ 2023-10-04 12:07  蕝戀  阅读(271)  评论(0编辑  收藏  举报