[Django] Learn Django Celery with RabbitMQ
热身练习
安装依赖
pip install celery sudo apt-get install rabbitmq-server
systemctl status rabbitmq-server
1. 配置好
from __future__ import absolute_import, unicode_literals import os from celery import Celery
# 绑定 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'proj.settings') app = Celery('proj') app.config_from_object('django.conf:settings', namespace='CELERY')
# 启动 app.autodiscover_tasks()
2. 启动服务
(venv) jeffrey@jeffrey-EnlargeDS:~/.../YT-Django-Celery-Series-Intro-Install-Run-Task$ sudo systemctl start rabbitmq-server [sudo] password for jeffrey: (venv) jeffrey@jeffrey-EnlargeDS:~/.../YT-Django-Celery-Series-Intro-Install-Run-Task$ celery -A proj worker -l info

(venv) jeffrey@jeffrey-EnlargeDS:~/.../YT-Django-Celery-Series-Intro-Install-Run-Task$ sudo systemctl start rabbitmq-server [sudo] password for jeffrey: (venv) jeffrey@jeffrey-EnlargeDS:~/.../YT-Django-Celery-Series-Intro-Install-Run-Task$ (venv) jeffrey@jeffrey-EnlargeDS:~/.../YT-Django-Celery-Series-Intro-Install-Run-Task$ (venv) jeffrey@jeffrey-EnlargeDS:~/.../YT-Django-Celery-Series-Intro-Install-Run-Task$ celery -A proj worker -l info [2023-05-12 01:41:29,670: WARNING/MainProcess] No hostname was supplied. Reverting to default 'localhost' -------------- celery@jeffrey-EnlargeDS v5.2.7 (dawn-chorus) --- ***** ----- -- ******* ---- Linux-5.19.0-41-generic-x86_64-with-glibc2.35 2023-05-12 01:41:29 - *** --- * --- - ** ---------- [config] - ** ---------- .> app: proj:0x7f02a03ec8b0 - ** ---------- .> transport: amqp://guest:**@localhost:5672// - ** ---------- .> results: disabled:// - *** --- * --- .> concurrency: 8 (prefork) -- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker) --- ***** ----- -------------- [queues] .> celery exchange=celery(direct) key=celery [tasks] . app1.tasks.add [2023-05-12 01:41:29,914: INFO/MainProcess] Connected to amqp://guest:**@127.0.0.1:5672// [2023-05-12 01:41:29,917: INFO/MainProcess] mingle: searching for neighbors [2023-05-12 01:41:29,922: WARNING/MainProcess] No hostname was supplied. Reverting to default 'localhost' [2023-05-12 01:41:30,999: INFO/MainProcess] mingle: all alone [2023-05-12 01:41:31,024: WARNING/MainProcess] /home/jeffrey/Desktop/web/YT-Django-Celery-Series-Intro-Install-Run-Task/venv/lib/python3.10/site-packages/celery/fixups/django.py:203: UserWarning: Using settings.DEBUG leads to a memory leak, never use this setting in production environments! warnings.warn('''Using settings.DEBUG leads to a memory [2023-05-12 01:41:31,024: INFO/MainProcess] celery@jeffrey-EnlargeDS ready.
3. 然后,创建具体的任务
from __future__ import absolute_import, unicode_literals from celery import shared_task @shared_task def add(x, y): return x + y
测试:
>>> from app1.tasks import add
>>> add.delay(4,4) <AsyncResult: fa9c5e8b-73e5-488e-862d-f1068b07423b> >>> add.delay(4,4) <AsyncResult: e4d8380f-2f01-4553-932e-120d8dea7ca6>
看到结果:

[2023-05-12 01:41:31,024: INFO/MainProcess] celery@jeffrey-EnlargeDS ready. [2023-05-12 01:43:07,056: INFO/MainProcess] Task app1.tasks.add[fa9c5e8b-73e5-488e-862d-f1068b07423b] received [2023-05-12 01:43:07,057: INFO/ForkPoolWorker-8] Task app1.tasks.add[fa9c5e8b-73e5-488e-862d-f1068b07423b] succeeded in 0.00028356400071061216s: 8 [2023-05-12 01:43:20,371: INFO/MainProcess] Task app1.tasks.add[e4d8380f-2f01-4553-932e-120d8dea7ca6] received [2023-05-12 01:43:20,372: INFO/ForkPoolWorker-8] Task app1.tasks.add[e4d8380f-2f01-4553-932e-120d8dea7ca6] succeeded in 0.00012868200064986013s: 8
异步创建多个任务:
>>> add.apply_async((3,3), countdown=10) <AsyncResult: a9a12796-0972-4f99-af72-8c20031e0138> >>> add.apply_async((3,3), countdown=1) <AsyncResult: 89676a07-1b98-42ba-8b15-ed6edea33308>
简单案例
邮箱配置
- Setting.py
Manage your Google Account.
创建如下,得到一个”密码“。
配置如下:

# gmail_send/settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'veryacademydemo@gmail.com'
EMAIL_HOST_PASSWORD = 'jaazriuaslxfpwpg'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = 'veryacademydemo@gmail.com'
- email.py
Email有不同的服务商。
from django.template import Context from django.template.loader import render_to_string from django.core.mail importEmailMessage from django.conf import settings def send_review_email(name, email, review): context = { 'name': name, 'email': email, 'review': review, } email_subject = 'Thank you for your review' email_body = render_to_string('email_message.txt', context)
# 定义具体的email的服务商 email = EmailMessage( email_subject, email_body, settings.DEFAULT_FROM_EMAIL, [email, ], ) return email.send(fail_silently=False)
异步任务 Celery
- task.py
定义asyn task。
from celery.decorators import task from celery.utils.log import get_task_logger from .email import send_review_email logger = get_task_logger(__name__) @task(name="send_review_email_task") # 这里没有用 shared_task def send_review_email_task(name, email, review): logger.info("Sent review email") return send_review_email(name, email, review)
提交表格
- forms.py
这里定义了form的变量。
from django import forms from task2.tasks import send_review_email_task class ReviewForm(forms.Form):
name = forms.CharField( label='Firstname', min_length=4, max_length=50, widget=forms.TextInput( attrs={'class': 'form-control mb-3', 'placeholder': 'Firstname', 'id': 'form-firstname'})) email = forms.EmailField( max_length=200, widget=forms.TextInput( attrs={'class': 'form-control mb-3', 'placeholder': 'E-mail', 'id': 'form-email'})) review = forms.CharField( label="Review", widget=forms.Textarea(attrs={'class': 'form-control', 'rows': '5'})) def send_email(self): send_review_email_task.delay( self.cleaned_data['name'], self.cleaned_data['email'], self.cleaned_data['review'])
- templates/review.html
form对应的html ui如下。
<div class="container-fluid"> <div class="row no-gutter"> <div class="col-md-6 bg-light"> <div class="login d-flex align-items-center py-5"> <div class="container"> <div class="row"> <div class="col-8 col-md-8 mx-auto"> <p class="h4 mb-4 font-weight-bold">Write a review</p> <form action="{% url 'reviews' %}" method="post">{% csrf_token %} {{ form.as_p }} <button class="btn btn-dark btn-block py-2 mb-4 mt-5 font-weight-bold" type="submit" value="Log-in">Submit</button> </form> </div> </div> </div> </div> </div> <div class="col-md-6 d-none d-md-flex bg-image"></div> </div> </div>
Request接口
- views.py
界面,但form的定义可以单独另外定义;这里只“invoke method":send_email()。
from task2.forms import ReviewForm from django.views.generic.edit import FormView from django.http import HttpResponse class ReviewEmailView(FormView): template_name = 'review.html'form_class= ReviewForm def form_valid(self, form): form.send_email() msg = "Thanks for the review!" return HttpResponse(msg)
- urls.py
from django.contrib import admin from django.urls import path from task2.views import ReviewEmailView urlpatterns = [ path('admin/', admin.site.urls), path('reviews/', ReviewEmailView.as_view(), name="reviews"), ]
Django + Celery 学习笔记
-
原理解析
Ref: Django+Celery学习笔记1——任务队列介绍
任务队列一般用于线程或计算机之间分配工作的一种机制。
任务队列的输入是一个称为任务的工作单元,有专门的职程(Worker)进行不断的监视任务队列,进行执行新的任务工作。
Celery 通过消息机制进行通信,通常使用中间人(Broker)作为 客户端 和职程(Worker)调节。启动一个任务,客户端向消息队列发送一条消息,然后中间人(Broker)将消息传递给一个职程(Worker),最后由职程(Worker)进行执行中间人(Broker)分配的任务。
Client 发送消息给 ”消息队列“ (代理是 RabbitMQ 或 Redis) --> Broker --> 分配 msg 给一个 Worker,并执行。
Celery 可以有多个职程(Worker)和中间人(Broker),用来提高Celery的高可用性以及横向扩展能力。
-
Celery特性描述
1、方便查看定时任务的执行情况, 如 是否成功, 当前状态, 执行任务花费的时间等.
2、使用功能齐备的管理后台或命令行添加,更新,删除任务.
3、方便把任务和配置管理相关联.
4、可选 a) 多进程, b) Eventlet 和 c) Gevent 三种模型并发执行.
5、提供错误处理机制.
6、提供多种任务原语, 方便实现任务分组,拆分,和调用链.
7、支持多种消息代理和存储后端.
8、Celery 是语言无关的.它提供了python 等常见语言的接口支持.
Ref: Python的eventlet使用与理解
Eventlet
Coroutine,翻译成”协程“,初始碰到的人马上就会跟上面两个概念联系起来。直接先说区别,Coroutine是编译器级的,Process和Thread是操作系统级的。Coroutine的实现,通常是对某个语言做相应的提议,然后通过后成编译器标准,然后编译器厂商来实现该机制。Process和Thread看起来也在语言层次,但是内生原理却是操作系统先有这个东西,然后通过一定的API暴露给用户使用,两者在这里有不同。Process和Thread是os通过调度算法,保存当前的上下文,然后从上次暂停的地方再次开始计算,重新开始的地方不可预期,每次CPU计算的指令数量和代码跑过的CPU时间是相关的,跑到os分配的cpu时间到达后就会被os强制挂起。Coroutine是编译器的魔术,通过插入相关的代码使得代码段能够实现分段式的执行,重新开始的地方是yield关键字指定的,一次一定会跑到一个yield对应的地方。
总之,对于Coroutine,是编译器帮助做了很多的事情,来让代码不是一次性的跑到底,而不是操作系统强制的挂起。代码每次跑多少,是可预期的。但是,Process和Thread,在这个层面上完全不同,这两个东西是操作系统管理的。
定时任务
celery==4.3.0 Django==2.2.2 django-celery-beat==1.5.0 django-celery-results==1.1.2 kombu==4.6.11 -- celery的依赖 PyMySQL==0.9.3 redis==3.2.1 python-crontab==2.5.1 -- 用于设置周期性被执行的指令
Project 配置
在 django的项目目录(djangocelerydemo)中 创建celery.py (与settings.py在同一级目录) 文件,当然你也可以命名成 celeryconfig.py 文件,
这个文件没有要求,为啥要创建这个文件呢?
因为,要将Celery与Django项目一起使用,必须首先定义Celery库的实例,也就是创建celery的应用。
文件放在此处,这种设置方法可以让celery自动在所有app中查找tasks文件,比较适合多人多APP同时开发的中大型项目 详情参考:Using Celery with Django
-
settings.py
CELERY_ENABLE_UTC = False # 不使用国际标准时间 CELERY_TIMEZONE = 'Asia/Shanghai' # 使用亚洲/上海时区 DJANGO_CELERY_BEAT_TZ_AWARE = False # 解决时区问题 CELERY_BROKER_URL = 'redis://127.0.0.1:6379/0' # 使用0号数据库 CELERY_BROKER_TRANSPORT = 'redis' # 使用redis作为中间件
CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler' # 自定义调度类,使用Django的ORM
CELERY_RESULT_BACKEND = 'django-db' # 任务结果,使用Django的ORM CELERY_ACCEPT_CONTENT = ['application/json'] # 设置任务接收的序列化类型 CELERY_TASK_SERIALIZER = 'json' # 设置任务序列化方式 CELERY_RESULT_SERIALIZER = 'json' # 设置结果序列化方式
CELERY_BEAT_SCHEDULE = { "scheduled_task": { "task": "task1.tasks.add", "schedule": 5.0, "args": (10, 10), }, "database": { "task": "task3.tasks.bkup", "schedule": 5.0, }, }
-
celery.py
from __future__ import absolute_import, unicode_literals import os from celery import Celery, platforms from django.utils.datetime_safe import datetime # 获取当前文件夹名,即为该 Django 的项目名 project_name = os.path.split(os.path.abspath('.'))[-1] project_settings = '%s.settings' % project_name print(project_settings) # 设置默认celery命令行的环境变量 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangocelerydemo.settings') # *** # 实例化 Celery,项目名称 app = Celery('djangocelerydemo') # *** # 解决时区问题 app.now = datetime.now # 使用 django 的 settings 文件配置 celery app.config_from_object('django.conf:settings', namespace='CELERY') # *** # 从所有应用中加载任务模块tasks.py app.autodiscover_tasks() # *** # 解决celery不能root用户启动的问题 platforms.C_FORCE_ROOT = True
from __future__ import absolute_import, unicode_literals import os from celery import Celery os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') # *** app = Celery('core') # *** app.config_from_object('django.conf:settings', namespace='CELERY') # *** app.autodiscover_tasks() # *** @app.task(bind=True) def debug_task(self): print('Request: {0!r}'.format(self.request))
# 引入celery实例对象 from __future__ import absolute_import, unicode_literals from djangocelerydemo.celeryconfig import app as celery_app __all__ = ('celery_app',) import pymysql pymysql.install_as_MySQLdb()
APP 配置
-
task定义 in App
# Create your tasks here from __future__ import absolute_import, unicode_literals from djangocelerydemo.celeryconfig import app @app.task def plan_task_1(): print("任务_1已运行!") return {"任务_1:success"} @app.task def plan_task_2(): print("任务_2已运行!") return {"任务_2:success"}
from __future__ import absolute_import, unicode_literals import sys from celery import shared_task from django.core.management import call_command import sys @shared_task def bkup(): sys.stdout = open('db.json', 'w') call_command('dumpdata', 'task3')
-
apps.py
from django.apps import AppConfig class CelerytestConfig(AppConfig): name = 'celerytest'
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律