我们以前一直使用k8s的cronjob来管理定时任务的。把定时任务相关的代码单独封装成一个pod,然后以cronjob的方法来触发。
虽然这个方法操作很简单,没有什么第三方资源的依赖(比如Redis),但是也有一个明显的缺点。
定时任务的代码脱离了Django代码,也就不能使用Django的很多功能了,只能通过DRF封装的API来跟Django的Server通信。
有的时候为了一个定时任务,要封装很多API,还要考虑鉴权等问题,也挺麻烦的,所以就在新项目中打算换一个方法来做定时任务的管理。
同时使用Python和Django的工程师估计基本都知道Celery,它是一个很好的异步任务框架。我上一次使用它还是2020年,发现这几年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的人应该对上面这个目录树非常熟悉,下面的内容都是基于这个目录树写的,所以需要记住这个目录树。
为了定义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 # 单个任务的最大运行时间,单位是秒
用celery做任务调度的时候可以最好能把每一次任务的结果记录下来,以便以后查阅,尤其是当任务没有按照预期运行的时候,这一点更加重要。
官网推荐使用django-celery-results做记录任务结果。
pip install django-celery-results
INSTALLED_APPS = (
...,
'django_celery_results',
)
注册之后还需要迁移数据库,python manage.py migrate django_celery_results
CELERY_RESULT_BACKEND = 'django-db' # 使用数据库做后端
CELERY_CACHE_BACKEND = 'django-cache' # 老实说,不知道这个缓存配置到底有什么作用,但是官网推荐使用这个配置,我也就留着了
CELERY_CACHE_BACKEND = 'default'
celery -A backend worker --loglevel=INFO
前面介绍了怎么样配置celery,现在celery有了,要怎么来管理定时任务呢?这个时候就要用到django-celery-beat了,它的使用比较简单。
pip install django-celery-beat
INSTALLED_APPS = (
...,
'django_celery_beat',
)
同样,注册之后要迁移数据库,python manage.py migrate django-celery-beat
celery -A proj beat -l INFO --scheduler django_celery_beat.schedulers:DatabaseScheduler
想要单独说明的是,很多人在使用django_celery_beat做定时任务管理的时候,喜欢把定时任务以cronjob的形式封装在代码中,但是我比较喜欢通过Django Admin页面在数据库中进行配置。
因为封装在代码中,以后如果想要修改定时任务,就需要重新写代码然后部署到环境中,不太友好,而且对于非技术人员来说,想要自己配置定时任务的可能性几乎为零。
这一部分内容比较简单,把Django启动,登录到Admin页面之后通过页面点击创建即可,不难,但是想写出来要接很多图,就不是很想写了。