开始使用Django和Celery
当我们在处理数据密集型的应用程序时,长期运行的任务会减慢应用程序和网站的加载时间。在这样的应用中,我们可以通过将一些工作从应用服务器卸载到消息代理服务器来改善应用的加载时间。
在本教程中,我们将学习如何在Django应用程序中使用Celery来执行长期运行的后台任务。
前提条件
为了继续学习本教程--你将需要以下条件。
- 在你的电脑中安装[Python]。
- 对[Python]有良好的理解。
- 在你的电脑上安装[Docker]。
工人
基于后台的任务服务器被称为workers 。在一个有网络服务器的应用程序中,我们可以让几个工作者在后台执行繁重的计算,并通过webhooks或回调将响应送回应用程序。
Celery消息队列
队列是一种基于先进先出原则工作的数据结构。我们通过消息队列将工作分配给工作者。工作者按照消息代理排队的顺序来处理任务。
队列确保每个工作者一次处理一个任务,而且只有一个工作者处理一个特定的任务。
Celery
Celery使Django应用程序中许多工作者的任务队列的实现变得更加容易。
Celery的功能。
- 将任务定义为python函数。
- 听取消息代理的新任务。
- 将任务分配给工作者。
- 监控工作者和任务。
项目设置
- 通过执行下面的命令创建一个工作目录。
mkdir project
- 通过执行下面的命令,将工作目录改为上面创建的
project目录。
cd project
- 用下面的命令创建一个Django应用程序。
django-admin startproject celerytask
- 用下面的命令创建一个安装软件包的虚拟环境。
virtualenv venv
- 通过执行下面的命令激活这个虚拟环境。
source venv/bin/activate
- 通过执行下面的命令将Django安装到我们在上面创建的虚拟环境中。
pip install django
- 通过执行下面的命令来迁移数据库模型。
cd celerytask
python manage.py migrate
- 现在我们可以将芹菜添加到我们的应用程序中。
pip install celery
- 通过执行下面的命令,启动Django网络服务器。
python manage.py runserver
- 导航到http://localhost:8000/,确认应用程序已经启动并运行。
向Django应用程序添加Celery配置
-
在存在
settings.py文件的项目文件夹中,创建一个名为celery.py的新Python文件。 -
将下面的代码片段添加到上面创建的文件中。
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
# setting the Django settings module.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'celery_task.settings')
app = Celery('celery_task')
app.config_from_object('django.conf:settings', namespace='CELERY')
# Looks up for task modules in Django applications and loads them
app.autodiscover_tasks()
上述配置使用Django设置创建了一个Celery应用程序。app.autodiscover_tasks() ,试图在我们所有的Django应用程序中发现一个名为task.py 的文件。
在settings.py 文件所在的包内的__init__.py 文件中,添加下面的代码片断。
from .celery import app as celery_app
__all__ = ['celery_app']
上述代码段在我们的应用程序每次启动时导入Celery。
创建一个Celery任务
让我们创建一个Django应用程序,从那里我们将设置Celery任务。
- 要创建一个新的Django应用程序,执行下面的命令。在该命令中,
task将是我们应用程序的名称。
python manage.py startapp task
-
在我们刚刚创建的
task目录下,创建一个名为task.py的Python文件。 -
将下面的代码片断添加到上面创建的
task.py。
import string
from django.contrib.auth.models import User
from django.utils.crypto import get_random_string
from celery import shared_task
@shared_task
def create_random_user_accounts(total):
for i in range(total):
username = 'user_{}'.format(get_random_string(10, string.ascii_letters))
email = '{}@example.com'.format(username)
password = get_random_string(50)
User.objects.create_user(username=username, email=email, password=password)
return '{} random users created with success!'.format (total)
上面的函数创建随机的用户账户。
Celery任务的方法签名如下所示。
from celery import shared_task
@shared_task
def name_of_your_function(optional_param):
pass # do some long running task
创建HTML文件
在项目的根目录下创建一个名为templates 的文件夹。在上面创建的templates 目录中,在其中创建一个名为task 的目录,因为它将存放我们的Djangotask 的HTML文件。
-
在上面创建的
task, 目录中,创建一个名为base.html的文件,并将下面的代码片段添加到其中。下面的代码片断是基础模板,其他模板文件将从该模板扩展。<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Celery tasks</title> <style type="text/css"> body { width: 800px; margin: 20px auto; } </style> </head> <body> <h1>Django Celery Task</h1> <a href="{% url 'users_list' %}">Users List</a> / <a href="{% url 'generate' %}">Generate Random Users</a> <hr> {% if messages %} {% for message in messages %} <p style="color: green">{{ message }}</p> {% endfor %} <hr> {% endif %} {% block content %} {% endblock %} </body> </html> -
在
task目录中,创建一个名为user_list.html的文件,并添加下面的代码片段。下面的代码片断将显示生成的随机用户列表。
{% extends 'task/base.html' %}
{% block content %}
<h2>Users List</h2>
<ul>
{% for user in object_list %}
<li>{{ user.username }} - {{ user.email }} - {{ user.date_joined }}</li>
{% empty %}
<li>No users. <a href="{% url 'generate' %}">Generate some random users.</a></li>
{% endfor %}
</ul>
{% endblock %}
- 在
task目录中,创建一个名为generate_random_user.html的文件,并添加下面的代码片断。下面的代码片段包含一个输入字段,我们将在这里指定要生成的随机用户的数量。
{% extends 'task/base.html' %}
{% block content %}
<h2>Generate Random Users</h2>
<form method="post">
{% csrf_token %}
<table>
{{ form }}
</table>
<button type="submit">Submit</button>
</form>
{% endblock %}
表格
在task 目录中(不是模板目录中的那个),创建一个名为form.py 的Python文件并添加下面的代码片段。
from django import forms
from django.core.validators import MinValueValidator, MaxValueValidator
class GenerateRandomUserForm(forms.Form):
total = forms.IntegerField(
validators=[
MinValueValidator(50),
MaxValueValidator(500)
]
)
查看
将下面的代码片断添加到task 的views.py 文件中。
from django.contrib.auth.models import User
from django.contrib import messages
from django.views.generic import ListView
from django.views.generic.edit import FormView
from django.shortcuts import redirect
from .form import GenerateRandomUserForm
from .task import create_random_user_accounts
# returns a list of generated user accounts
class UsersListView(ListView):
template_name = 'task/user_list.html'
model = User
# A page with the form where we can input the number of accounts to generate
class GenerateRandomUserView(FormView):
template_name = 'task/generate_random_user.html'
form_class = GenerateRandomUserForm
def form_valid(self, form):
total = form.cleaned_data.get('total')
create_random_user_accounts.delay(total)
messages.success(self.request, 'We are generating your random users! Wait a moment and refresh this page.')
return redirect('users_list')
在上面的代码片段中,注意我们没有调用create_random_user_accounts 方法,而是调用了create_random_user_accounts.delay(total) 。这指示Celery作为一个后台进程执行任务。
URLs
用下面的代码片断更新urls.py 。
from django.conf.urls import url
from django.contrib import admin
from django.urls import path
from task.views import GenerateRandomUserView, UsersListView
urlpatterns = [
path('admin/', admin.site.urls),
url('users/', UsersListView.as_view(), name='users_list'),
url('generate/', GenerateRandomUserView.as_view(), name='generate')
]
设置基础模板目录
在settings.py 文件中,用下面的代码片断更新TEMPLATES 部分。
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'], # We are adding in the directory where our templates will be stored.
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
- 执行下面的命令,创建一个
requirements.txt文件,我们将在构建Docker镜像时使用。
pip freeze > requirements.txt
requirements.txt该文件包含我们的应用程序所需的所有软件包。
容器化
创建Docker文件
在项目根目录下,project/celerytask ,创建一个名为Dockerfile 的文件,并添加以下代码片段。
# pull the official base image
FROM python:3.9.5-alpine
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip install -r requirements.txt
# copy project
COPY . /usr/src/app/
RUN python manage.py migrate
EXPOSE 8000
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
上面的代码片段包含了将用于创建Docker镜像的指令。
创建一个Docker compose部署文件
在工作目录project ,创建一个名为docker-compose.yml 的文件,并添加以下代码片段。
version: '3.3'
services:
web:
build: ./celerytask #path to the root project folder
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./celerytask:/usr/src/app/
ports:
- 8000:8000 # sets the port that maps to internal port in docker container
environment:
- DEBUG=1
- SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
- DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
- CELERY_BROKER=redis://redis:6379/0
- CELERY_BACKEND=redis://redis:6379/0
depends_on:
- redis
celery:
build: ./celerytask
command: celery worker --app=core --loglevel=info --logfile=logs/celery.log # Command used to start the Celery worker in the Docker container
volumes:
- ./celerytask:/usr/src/app
environment:
- DEBUG=1
- SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
- DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
- CELERY_BROKER=redis://redis:6379/0
- CELERY_BACKEND=redis://redis:6379/0
# depends on show that celery worker service requires the web service and the redis service to run
depends_on:
- web
- redis
redis:
image: redis:6-alpine
在上面的docker-compose.yml 文件中,我们有3个服务。
web- 是运行我们应用程序代码的服务。celery- 是运行Celery工作者的服务。redis- 是运行Redis服务器的服务。Redis是一个密钥对数据存储,将被用来存储排队的事件。
构建镜像
在项目根目录下,执行下面的命令来创建一个镜像。
docker build -t celerytask .
-t为将要创建的镜像添加一个标签 。celerytask.命令的结尾表示Dockerfile在当前目录下,该命令正在执行。
为了验证镜像是否已经成功创建,请执行下面的命令。
karen@karen:~$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
celerytask latest a9803f267258 About a minute ago 172MB
使用Docker compose进行部署
执行下面的命令,创建并启动我们在docker-compose.yml 文件中声明的服务。
docker-compose up -d --build
测试
- 打开你的浏览器,导航到http://localhost:8000/generate,输入要生成的用户数量。

- 点击生成用户后,Celery会安排一个后台任务,在后台生成随机的用户账户,如下图所示。

- 几秒钟后刷新
users,我们看到一个随机生成的用户列表,如下图所示。

请确保在启动celery工作者之前运行迁移。
总结
现在你已经学会了如何将Celery集成到Django应用程序中并执行定期任务,创建一个Django应用程序,每隔1小时运行一个备份脚本来备份自己。