利用Django和Celery管理定时任务


同步发表于个人站点: http://panzhixiang.cn/article/2023/3/16/68.html

一、背景介绍

我们以前一直使用k8s的cronjob来管理定时任务的。把定时任务相关的代码单独封装成一个pod,然后以cronjob的方法来触发。

虽然这个方法操作很简单,没有什么第三方资源的依赖(比如Redis),但是也有一个明显的缺点。

定时任务的代码脱离了Django代码,也就不能使用Django的很多功能了,只能通过DRF封装的API来跟Django的Server通信。
有的时候为了一个定时任务,要封装很多API,还要考虑鉴权等问题,也挺麻烦的,所以就在新项目中打算换一个方法来做定时任务的管理。

同时使用Python和Django的工程师估计基本都知道Celery,它是一个很好的异步任务框架。我上一次使用它还是2020年,发现这几年Celery的使用方法发生了一些变化,在网上找了一圈也没有找到很好的中文资料,所以自己写一篇相关的博客,希望能给以后需要查询相关信息的人提供一点帮助。

二、Celery配置

在配置Celery之前需要先安装,pip install celery, 接下来就开始配置了。

在正式开始介绍配置之前,我们需要一些假设,以便下面的文字可以表述的更清楚。

我们以django-admin startproject proj创建一个Django项目,Django版本应当>=3.0, 创建成功之后我们会得到如下的一个目录结构:

proj
├── manage.py
└── proj
    ├── asgi.py
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

熟悉Django的人应该对上面这个目录树非常熟悉,下面的内容都是基于这个目录树写的,所以需要记住这个目录树。

1. 定义Celery实例

为了定义Celery实例,需要在上面的目录树中创建一个文件: proj/proj/celery.py。
这个文件名是celery.py,跟settings.py在同一层目录。

内容如下,我把一些很重要的信息以注释的形式写在代码里了,注意查看。

import os
from celery import Celery


# 这个配置可以避免在其他的tasks.py中初始化django配置,虽然不是必须的,但是强烈建议要有这个配置
os.environ.setdefault(
    'DJANGO_SETTINGS_MODULE', 'proj.settings'
)

# 这个就是从环境变量中获取redis的地址,我这里使用redis作为broker
REDIS_HOST = os.getenv('REDIS_HOST', 'localhost:6379')
app = Celery(
    'proj',  # 第一个参数是为celery的实例起了一个名字,这里叫做proj
    backend='redis://' + REDIS_HOST + '/1',
    broker='redis://' + REDIS_HOST + '/0',
)

# 可以用这个方法批量配置celery,
# 这几个配置在一帮的场景中就足够使用了
# 另外,其实还有几种其他方法来配置celery,但是我觉得这个方法对于不是非常大的项目来说就足够了。
app.conf.update(
    task_serializer='json',
    accept_content=['json'],  # Ignore other content
    result_serializer='json',
    enable_utc=True,
)

# 这一行会从django的settings文件中获取一些celery的配置
# namespace等于CELERY的意思是settings中以 “CELERY_” 开头的配置都会被识别为celery的配置
app.config_from_object('django.conf:settings', namespace='CELERY')

# 会自动发现所有Django app中的任务
app.autodiscover_tasks()


@app.task(bind=True)
def debug_task(self):
    print(f'Request: {
      
      self.request!r}')

除了上面这个配置,还有两个地方需要配。
首先是需要在proj/proj/__init__.py中添加以下内容:

from .celery import app as celery_app


__all__ = ('celery_app',)

它的作用是在启动Django的时候自动加载celery。

还有一个就是需要在django的settings中添加celery的配置,也就是上面代码中app.config_from_object('django.conf:settings', namespace='CELERY') 提到的部分。

CELERY_TASK_TRACK_STARTED = True
CELERY_TASK_TIME_LIMIT = 30 * 60  # 单个任务的最大运行时间,单位是秒

2. 记录任务的结果

用celery做任务调度的时候可以最好能把每一次任务的结果记录下来,以便以后查阅,尤其是当任务没有按照预期运行的时候,这一点更加重要。

官网推荐使用django-celery-results做记录任务结果。

  1. 安装
    pip install django-celery-results
  2. 注册
    django-celery-results是一个单独的django的app,所以需要在settings.py注册一下
    INSTALLED_APPS = (
        ...,
        'django_celery_results',
    )
    
    注册之后还需要迁移数据库,
    python manage.py migrate django_celery_results
  3. 配置
    django-celery-results只是一个帮助自动存储任务结果的包,最终数据还需要一个地方落地,有很多地方都可以用来存储任务结果,比如数据库、本地文件系统,redis等等,我这里使用数据库,也比较推荐使用数据库。
    在django的setting.py中添加一下配置:
    CELERY_RESULT_BACKEND = 'django-db'  # 使用数据库做后端
    CELERY_CACHE_BACKEND = 'django-cache'  # 老实说,不知道这个缓存配置到底有什么作用,但是官网推荐使用这个配置,我也就留着了
    CELERY_CACHE_BACKEND = 'default'
    
  4. 启动
    注意这个命令要在第一层proj目录下运行,不然会报错,提示找不到配置文件之类的错误
    celery -A backend worker --loglevel=INFO
    

三、定时任务配置

前面介绍了怎么样配置celery,现在celery有了,要怎么来管理定时任务呢?这个时候就要用到django-celery-beat了,它的使用比较简单。

1. 配置django-celery-beat

  1. 安装
    pip install django-celery-beat
  2. 注册
    在django的settings.py中进行注册
    INSTALLED_APPS = (
        ...,
        'django_celery_beat',
    )
    
    同样,注册之后要迁移数据库,
    python manage.py migrate django-celery-beat
  3. 启动
    注意这个命令要在第一层proj目录下运行,不然会报错,提示找不到配置文件之类的错误
    celery -A proj beat -l INFO --scheduler django_celery_beat.schedulers:DatabaseScheduler
    

想要单独说明的是,很多人在使用django_celery_beat做定时任务管理的时候,喜欢把定时任务以cronjob的形式封装在代码中,但是我比较喜欢通过Django Admin页面在数据库中进行配置。

因为封装在代码中,以后如果想要修改定时任务,就需要重新写代码然后部署到环境中,不太友好,而且对于非技术人员来说,想要自己配置定时任务的可能性几乎为零。

2. 通过Django Admin设置具体的定时任务

这一部分内容比较简单,把Django启动,登录到Admin页面之后通过页面点击创建即可,不难,但是想写出来要接很多图,就不是很想写了。

四、参考

  1. First Steps with Django
  2. Task result backend settings

猜你喜欢

转载自blog.csdn.net/u013117791/article/details/129584654