Django初步使用Celery处理耗时任务和定时任务问题

Celery是Python开发分布式任务列队的处理库。可以异步分布式地异步处理任务,也可定时执行任务等等。

通常我们可以使用celery在Django执行一些比较耗时的任务(例如发邮件)和后台任务(例如爬虫和更新服务器缓存)。

在Django中使用有两种方式:

  • 1)使用django-celery应用
  • 2)直接使用Celery

1、Celery方式的选择

这里Celery的中间人,采用Redis。也可以用Django自身的mongodb等。Celery的中间人你可以理解为在Celery执行过程中的数据支持。保存列队记录、执行记录等等。

需要安装celery-with-redis,执行命令

pip install celery-with-redis

该命令会自动安装redis(python库操作redis的库)、celery、kombu、billiard、amqp、vine和celery-with-redis相关库。

注意,这里pip安装的redis是python操作redis的库,非redis数据库。

redis数据库需要独立安装,在cmd里输入 pip3 install redis

先说说django-celery的方式吧。这种方式就是通过manage.py启动celery。通常先被提到的方案是不会采用。用pip安装django-celery,在settings引用djcelery应用。

再更新数据库:

python manage.py makemigrations djcelery
 
python manage.py migrate djcelery

查看数据库,会发现多了很多相关的表,显得十分多余了就。

djcelery还有个用途是在admin后台动态添加定时任务。这个功能也是比较鸡肋,维护不方便而且可能造成各种不可预知的问题。

所以建议直接使用Celery管理Django中的任务(第二种方式)。这种方式也是Celery官网推荐的方式。

2、Django简单项目准备

这里我也简单做一个示例。

首先,确保celery和redis已经安装好了,并且已经启动了Redis服务。

另外,有个已经搭建好了Django项目。

作为示例,简单project和简单app如下:

Django初步使用Celery处理耗时任务和定时任务问题_第1张图片

左边侧边栏是该django的目录结构,右边是myapp中的Blog模型。

再进入后台随便加了两条数据:

Django初步使用Celery处理耗时任务和定时任务问题_第2张图片

为了测试,一切从简。views.py写了一个响应方法:

#views.py
from django.shortcuts import render
 
from django.http import HttpResponse
 
 
from models import Blog
 
import json
 
 
def home(request):
 
    data = list(Blog.objects.values('caption'))
 
    return HttpResponse(json.dumps(data), content_type = 'application/json')

django项目的urls.py加了一条首页的url路由设置:

#urls.py
 
from django.conf.urls import url
 
from django.contrib import admin
 
from myapp.
 
 
urlpatterns = [
 
    url(r'^admin/', admin.site.urls),
 
    url(r'^$', 'myapp.views.home', name='home')
 
]

运行django项目:

python manage.py runserver

打开http://localhost:8000/,如下效果:

Django初步使用Celery处理耗时任务和定时任务问题_第3张图片

3、Django加入Celery

现打开首页要执行一个收集访客数据,发送邮件等操作。这是一个耗时任务,若放在home处理方法中执行,用户打开首页会很慢。用户体验不好,很可能不会等到页面打开。

通常这个耗时任务可以多线程处理或者异步处理。我们模拟一个耗时任务,丢给Celery异步处理。

先模拟耗时任务,打开views.py,修改如下:

#views.py
 
from django.shortcuts import render
 
from django.http import HttpResponse
 
 
from .models import Blog
 
import json
 
import time
 
 
def sendmail(email):
 
    print('start send email to %s' % email)
 
    time.sleep(5) #休息5秒
 
    print('success')
 
    return True
 
 
def home(request):
 
    #耗时任务,发送邮件
 
    sendmail('[email protected]')
 
 
    #其他行为
 
    data = list(Blog.objects.values('caption'))
 
    return HttpResponse(json.dumps(data), content_type = 'application/json')

如此一来,至少需要再多等待5秒,才可以打开网页。

打开settings.py所在的文件夹,新建celery.py文件。

加入如下代码(注意,因为celery-with-django版本限制,我安装的celery版本为3.1.25。可能celery4.x的版本代码不同):

#celery.py
 
from __future__ import absolute_import, unicode_literals
 
 
from celery import Celery
 
from django.conf import settings
 
import os
 
 
#获取当前文件夹名,即为该Django的项目名
 
project_name = os.path.split(os.path.abspath('.'))[-1]
 
project_settings = '{}.settings'.format(project_name)
 
 
#设置环境变量
 
os.environ.setdefault('DJANGO_SETTINGS_MODULE', project_settings)
 
 
#实例化Celery
 
app = Celery(project_name)
 
 
#使用django的settings文件配置celery
 
app.config_from_object('django.conf:settings')
 
 
#Celery加载所有注册的应用
 
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

这个文件还没被加载,接着打开settings.py同个目录下的__init__.py文件。让运行该Django项目的时候,加载该文件配置Celery。

修改代码如下:

#__init__.py
 
from __future__ import absolute_import, unicode_literals
 
 
#引入celery实例对象
 
from .celery import app as celery_app

还需在settings.py中设置celery,尤其是中间人的设置。若不设置中间人,会提示无法连接中间人的错误。

在settings.py文件中添加如下设置:

#celery settings
 
#celery中间人 redis://redis服务所在的ip地址:端口/数据库号
 
BROKER_URL = 'redis://localhost:6379/0'
 
#celery结果返回,可用于跟踪结果
 
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
 
 
#celery内容等消息的格式设置
 
CELERY_ACCEPT_CONTENT = ['application/json',]
 
CELERY_TASK_SERIALIZER = 'json'
 
CELERY_RESULT_SERIALIZER = 'json'
 
 
#celery时区设置,使用settings中TIME_ZONE同样的时区
 
CELERY_TIMEZONE = TIME_ZONE

4、把耗时任务丢给celery处理

上面views.py中有个耗时任务sendmail。在myapp应用中新建文件tasks.py,将sendmail方法剪切到该文件中并用定义为celery任务。

tasks.py文件如下代码:

#tasks.py
 
from celery.decorators import task 
 
import time
 
 
@task
def sendmail(email):
 
    print('start send email to %s' % email)
 
    time.sleep(5) #休息5秒
 
    print('success')
 
    return True

在原有的方法上加上celery装饰器task(或者也可以通过前面添加的celery_app给sendmail方法加装饰器):

#tasks.py
#myproject是当前django的项目名
 
from myproject import celery_app
import time
 
@celery_app.task
def sendmail(email):
 
    print('start send email to %s' % email)
 
    time.sleep(5) #休息5秒
 
    print('success')
 
    return True

另外原先的views.py修改如下:

#views.py
 
from django.shortcuts import render
 
from django.http import HttpResponse
 
 
from .models import Blog
 
from .tasks import sendmail #引用tasks.py文件的中sendmail方法
 
import json
 
 
def home(request):
 
    #耗时任务,发送邮件(用delay执行方法)
 
    sendmail.delay('[email protected]')
 
 
    #其他行为
 
    data = list(Blog.objects.values('caption'))
 
    return HttpResponse(json.dumps(data), content_type = 'application/json')

5、本地启动celery并测试

启动celery之前,确保已经安装redis和启动redis服务。

本地开发环境运行redis-cli看是否可以正常连接,若不行,再手工执行redis-server命令并保持窗口即可。

Django初步使用Celery处理耗时任务和定时任务问题_第4张图片

接着,启动celery worker。这个worker是用于异步执行任务的“工作者”。

进入manage.py文件所在的目录,执行如下命令:

Celery -A myproject worker -l info

出现如下窗口和消息,则正常执行。

Django初步使用Celery处理耗时任务和定时任务问题_第5张图片

celery worker会扫描django项目中有哪些task任务,并加入进来。见上图的蓝色字下[tasks]字样。

最后,再启动django服务器。这个大家熟悉的python manage.py runserver。

打开首页,可以发现没有5秒等待立即得到首页内容。查看celery worker,可看到执行sendmail方法的消息。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

你可能感兴趣的:(Django初步使用Celery处理耗时任务和定时任务问题)