# djangotask **Repository Path**: aqu415/djangotask ## Basic Information - **Project Name**: djangotask - **Description**: 使用 django-celery-beat 框架搭建周期任务demo - **Primary Language**: Python - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 5 - **Forks**: 3 - **Created**: 2021-07-10 - **Last Updated**: 2025-06-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 前言 本文只研究界面配置管理周期任务cron场景,如果想要学习异步任务和硬编码cron周期任务的请搜索其他资料。 极力推荐好文 [知乎](https://zhuanlan.zhihu.com/p/369639432) 本文代码 [gitee](https://gitee.com/aqu415/djangotask) 下载 ### 吐槽 ``` 天下小白苦python久矣,各种模块版本不兼容在最近一次搭建一个定时任务的过程中体现的淋漓尽致; 在网上查了很多资料,真心写的能让入门小白看明白的文章凤毛麟角; 最开始想用celery和django-celery来搭建周期任务的,网上各种写法的都有看得头晕; 有的说celery什么版本后不支持widows;这个特性让我感到很震惊,毕竟还是有很大部分开发还是用的widows作为操作系统来开发的吧! 在经过大量的资料查阅和经历各种版本不匹配后,我最终选择了 django-celery-beat这个框架来完成周期任务的搭建; 第一是这个框架支持在界面配置任务的执行周期(逻辑和定时器解耦),很符合我最开始的预期; 第二是在界面配置task对于的cron表达式能够立即生效,不用重启; ``` ### 角色说明 ``` 最开始的时候我在寻求一种解决方案: 服务端只启动一个(django既做界面展示角色,也做消息生产角色),然后消费端启动多个,中间用redis作为消息队列; 经过大量查资料,结果证明在python 和celery的组合拳场景下不是我想的那样,而是: 1、django界面角色只能说是周期任务的入口,操作数据库的入口 (在周期硬编码的情况下是不是连django前台页面都可以不用启动?这个有待确认) 2、生产者需要单独启动(一个叫beat的组件) 3、消费者单独启动,即worker 这样设计我估计是为了解耦,扩展; 但是这样的设计就会出现一个问题,同一份代码要在不同的地方启动多次:django启动一个,beat启动一个,worker启动n个; 这个跟现有比较流行的任务调度框架比如xxl-job的思想还是不一样的; 首先需要理解这一点差异,才会更好的接受他的一些操作; ``` ### 版本说明 在python世界里,版本不对寸步难行(pip其实可以做的更好) | 名称 | 版本号 | 备注 | | ---- | ---- | ---- | | python | 3.7.9 | | | django | 2.2 | [django和python版本关系](https://blog.csdn.net/weixin_41924879/article/details/108774784) | |django-celery-beat | 2.2.1 | 安装了django-celery-beat 会自动把 celery(5.1.2)安装好 | | mysqlclient | 2.0.3 | dj-celery-beta数据库操作时需要 | | redis | 3.5.3 | | | eventlet | 0.31.0 | 可选,windows下运行celery 4以后版本,还需额外安装eventlet库 | ### 环境搭建 #### 新建项目和app 新建一个项目(如果不会创建django项目的就看看其他资料),结构如下: 项目名:djangotask 在项目下新建一个app:xxx ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210710091409629.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FxdTQxNQ==,size_16,color_FFFFFF,t_70) #### settings.py配置 + 将xxx app 和 django_celery_beat app 添加到 INSTALLED_APPS 变量 ``` INSTALLED_APPS = [ ...省略了已经默认有的一些app.. "xxx", 'django_celery_beat', ] ``` + 配置数据库,我这里用的是本地mysql ``` # https://docs.djangoproject.com/en/2.2/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'task', 'USER': 'root', 'PASSWORD': 'root', 'HOST': '127.0.0.1', 'PORT': '3306', } } ``` + 执行数据库migrate,因为django-celery-beat带了一些表结构 ``` 在manage.py所在目录分别执行: python manage.py magemigrations python manage.py migrate 效果如下 ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210710093123335.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FxdTQxNQ==,size_16,color_FFFFFF,t_70) 这时我们再看数据库是不是已经初始化好了表结构(里面有django和dj-celery-beat相关的表): ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210710093439917.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FxdTQxNQ==,size_16,color_FFFFFF,t_70) #### django-celery-beat 配置 + 新建一个统筹celery配置的文件 在项目目录djangotask/djangotask/目录下新建 celery.py,这个文件主要用来创建一个Celery对象,即第11行代码,然后加载Celery对象所需的配置即第14行 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210710120056658.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FxdTQxNQ==,size_16,color_FFFFFF,t_70) 代码: ``` from __future__ import absolute_import, unicode_literals import os from celery import Celery # 设置django环境 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangotask.settings') # 创建一个Celery app app = Celery('djangotask') # 使用CELERY_ 作为前缀,在celeryconfig.py中写配置 app.config_from_object('djangotask.celeryconfig', namespace='CELERY') # 发现任务文件每个app下的task.py app.autodiscover_tasks() ``` + 定义 Celery 对象所需的配置即 celeryconfig.py文件,这个文件里的内容也可以写到项目的 settings.py里面去,因为Celery 对象的配置文件位置可以在 celery.py 里进行指定;比起把这些配置写在settings.py里,我更喜欢单独存放; 这个文件主要定义比如redis,序列化,worker等相关信息 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210710120546216.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FxdTQxNQ==,size_16,color_FFFFFF,t_70) + 使能 celery.py 注意:celery.py不会被框架自动加载进来,这里我们通过工程的init.py来把celery引进来 。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210710120908983.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FxdTQxNQ==,size_16,color_FFFFFF,t_70) 代码: ``` from __future__ import absolute_import, unicode_literals from .celery import app as celery_app ``` #### 定义任务 从上面的配置看,djcelery-beta会自动去扫描每个app目录下是否有 tasks.py 这么一个文件,这个文件就是我们编写任务具体内容的地方;我们在xxx app下新建一个tasks.py: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210710130849406.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FxdTQxNQ==,size_16,color_FFFFFF,t_70) 代码: ``` from __future__ import absolute_import, unicode_literals from celery import shared_task @shared_task def add(x, y): print("invoke add #####################################") return x + y @shared_task def mul(x, y): print("invoke add #####################################") print("invoke mul") return x * y ``` #### createsuperuser 创建超级用户 创建用户主要作用是对任务进行管理,在manage.py目录下执行命令: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210710100252551.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FxdTQxNQ==,size_16,color_FFFFFF,t_70) ``` python manage.py createsuperuser ``` ### 测试 #### 进入管理后台界面 启动django界面应用后访问 http://localhost:8000/admin/ 输入刚刚的用户名密码 ![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/dd8f3b46c207477341166ba1009d9176.png) 这里我们进入“间隔任务”超链接,定义一个每分钟执行一次的任务; ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210710123951625.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FxdTQxNQ==,size_16,color_FFFFFF,t_70) 然后选择保存; 上面只是定义了一个周期计划,还要把周期计划和task进行绑定,我们进入“任务定义”超链接 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210710124423959.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FxdTQxNQ==,size_16,color_FFFFFF,t_70) 然后点击保存 #### 启动beat beta是一个生产者角色,是单独运行;从这里就可以看出,生产者完全不依赖django web界面,web界面作用是做配置! idea开启一个命令行窗口,进入 manage.py 文件所在的位置,执行命令: ``` celery -A djangotask beat -l info ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210710121629772.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FxdTQxNQ==,size_16,color_FFFFFF,t_70) 正常启动后,beat会定时的向redis发送消息: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210710124556448.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FxdTQxNQ==,size_16,color_FFFFFF,t_70) #### 启动worker 再开启一个命令行窗口,进入 manage.py 文件所在的位置,执行命令: ``` # Linux下测试,启动Celery Celery -A djangotask worker -l info # Windows下测试,启动Celery Celery -A djangotask worker -l info -P eventlet # 如果Windows下Celery不工作,输入如下命令 Celery -A djangotask worker -l info --pool=solo 监听queue1队列 Celery -A djangotask worker -l info -Q queue1 --pool=solo 监听queue2队列 Celery -A djangotask worker -l info -Q queue2 --pool=solo ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210710130353590.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FxdTQxNQ==,size_16,color_FFFFFF,t_70) 感兴趣的同学可以在 [gitee](https://gitee.com/aqu415/djangotask) 下载 ### 官网 + celery Version 4.0 introduced new lower case [settings](https://docs.celeryproject.org/en/stable/userguide/configuration.html#configuration) and setting organization. [celery Version 4.0 introduced new lower case settings](./celery4.0-config-change.md) #### 关于我 如果觉得文章内容还可以,可关注我获得更多优质的输出,您的关注是我最大的动力; ![在这里插入图片描述](https://img-blog.csdnimg.cn/799d40c4e0e140d4910187c1ce2c54b2.jpeg)