Django Celery异步任务队列的实现


Posted in Python onJuly 24, 2019

背景

在开发中,我们常常会遇到一些耗时任务,举个例子:

上传并解析一个 1w 条数据的 Excel 文件,最后持久化至数据库。

在我的程序中,这个任务耗时大约 6s,对于用户来说,6s 的等待已经是个灾难了。

比较好的处理方式是:

  1. 接收这个任务的请求
  2. 将这个任务添加到队列中
  3. 立即返回「操作成功,正在后台处理」的字样
  4. 后台消费这个队列,执行这个任务

我们按照这个思路,借助 Celery 进行实现。

实现

本文所使用的环境如下:

  • Python 3.6.7
  • RabbitMQ 3.8
  • Celery 4.3

使用 Docker 安装 RabbitMQ

Celery 依赖一个消息后端,可选方案有 RabbitMQ, Redis 等,本文选用 RabbitMQ 。

同时为了安装方便,RabbitMQ 我直接使用 Docker 安装:

docker run -d --name anno-rabbit -p 5672:5672 rabbitmq:3

启动成功后,即可通过 amqp://localhost 访问该消息队列。

安装并配置 Celery

Celery 是 Python 实现的工具,安装可以直接通过 Pip 完成:

pip install celery

同时假设当前我的项目文件夹为 proj ,项目名为 myproj ,应用名为 myapp

安装完成后,在 proj/myproj/ 路径下创建一个 celery.py 文件,用来初始化 Celery 实例:

proj/myproj/celery.py

from __future__ import absolute_import, unicode_literals
import os
from celery import Celery, platforms

# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproj.settings')

app = Celery('myproj',
       broker='amqp://localhost//',
       backend='amqp://localhost//')

# Using a string here means the worker don't have to serialize
# the configuration object to child processes.s
# - namespace='CELERY' means all celery-related configuration keys
#  should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')

# Load task modules from all registered Django app configs.
app.autodiscover_tasks()

然后在 proj/myproj/__init__.py 中添加对 Celery 对象的引用,确保 Django 启动后能够初始化 Celery:

proj/myproj/__init__.py

from __future__ import absolute_import, unicode_literals

# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

__all__ = ('celery_app',)

无其他特殊配置的话,Celery 的基本配置就是这些。

编写一个耗时任务

为了模拟一个耗时任务,我们直接创建一个方法,使其「睡」10s ,并将其设置为 Celery 的任务:

proj/myapp/tasks.py

import time
from myproj.celery import app as celery_app

@celery_app.task
def waste_time():
  time.sleep(10)
  return "Run function 'waste_time' finished."

启动 Celery Worker

Celery 配置完成,并且任务创建成功后,我们以异步任务的模式启动 Celery :

celery -A myproj worker -l info

注意到我强调了异步模式,是因为 Celery 除了支持异步任务,还支持定时任务,因此启动时候要指明。

同时要注意,Celery 一旦启动,对 Task(此处为 waste_time) 的修改必须重启 Celery 才会生效。

任务调用

在请求处理的逻辑代码中,调用上面创建好的任务:

proj/myapp/views.py

from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from .tasks import waste_time

@require_http_methods(["POST"])
def upload_files(request):
  waste_time.delay()
  # Status code 202: Accepted, 表示异步任务已接受,可能还在处理中
  return JsonResponse({"results": "操作成功,正在上传,请稍候..."}, status=202)

调用 waste_time.delay() 方法后, waste_time 会被加入到任务队列中,等待空闲的 Celery Worker 调用。

效果

当我们发送请求时,这个接口会直接返回 {"results": "操作成功,正在上传,请稍候..."} 的响应内容而非卡住十秒,用户体验要好许多。

总结

用 Celery 处理这种异步任务是 Python 常用的方法,虽然实际执行成功耗时不变甚至有所增加(如 Worker 繁忙导致处理滞后),但是对于用户体验来说更容易接受,点击上传大文件后可以继续处理其他事务,而不需要在页面等待。
Celery 还有更多用法本文未介绍到,其文档已经非常详尽,有需要可直接参考。

参考

http://docs.celeryproject.org/en/latest/django/first-steps-with-django.html

https://hub.docker.com/_/rabbitmq

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python实现子类调用父类的方法
Nov 10 Python
Python中实现结构相似的函数调用方法
Mar 10 Python
Python实现遍历数据库并获取key的值
May 17 Python
浅谈Python中用datetime包进行对时间的一些操作
Jun 23 Python
用matplotlib画等高线图详解
Dec 14 Python
Python读取mat文件,并转为csv文件的实例
Jul 04 Python
Python 确定多项式拟合/回归的阶数实例
Dec 29 Python
Python装饰器语法糖
Jan 02 Python
python匿名函数用法实例分析
Aug 03 Python
python向图片里添加文字
Nov 26 Python
Django单元测试中Fixtures的使用方法
Feb 26 Python
python中最小二乘法详细讲解
Feb 19 Python
python如何统计代码运行的时长
Jul 24 #Python
Django时区详解
Jul 24 #Python
详解Django定时任务模块设计与实践
Jul 24 #Python
Python3中urlencode和urldecode的用法详解
Jul 23 #Python
对python3中的RE(正则表达式)-详细总结
Jul 23 #Python
python正则表达式匹配不包含某几个字符的字符串方法
Jul 23 #Python
python使用百度文字识别功能方法详解
Jul 23 #Python
You might like
模仿OSO的论坛(三)
2006/10/09 PHP
php笔记之:初探PHPcms模块开发介绍
2013/04/26 PHP
php socket客户端及服务器端应用实例
2014/07/04 PHP
destoon实现调用自增数字从1开始的方法
2014/08/21 PHP
ThinkPHP结合AjaxFileUploader实现无刷新文件上传的方法
2014/10/29 PHP
PHP+redis实现的悲观锁机制示例
2018/06/12 PHP
jquery 最简单易用的表单验证插件
2010/02/27 Javascript
AngularJS使用ng-repeat指令实现下拉框
2016/08/23 Javascript
vue.js将unix时间戳转换为自定义时间格式
2017/01/03 Javascript
谈谈vue中mixin的一点理解
2017/12/12 Javascript
关闭Vue计算属性自带的缓存功能方法
2018/03/02 Javascript
vue根据进入的路由进行原路返回的方法
2018/09/26 Javascript
小程序实现分类页
2019/07/12 Javascript
微信分享invalid signature签名错误踩过的坑
2020/04/11 Javascript
jquery实现穿梭框功能
2021/01/19 jQuery
[01:34:42]NAVI vs EG 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
python利用datetime模块计算时间差
2015/08/04 Python
python爬虫入门教程--HTML文本的解析库BeautifulSoup(四)
2017/05/25 Python
Python3之文件读写操作的实例讲解
2018/01/23 Python
Python 中的Selenium异常处理实例代码
2018/05/03 Python
Python实现爬虫从网络上下载文档的实例代码
2018/06/13 Python
python 循环读取txt文档 并转换成csv的方法
2018/10/26 Python
python读出当前时间精度到秒的代码
2019/07/05 Python
python opencv 简单阈值算法的实现
2019/08/04 Python
CSS3中文字镂空、透明值、阴影效果设置示例小结
2016/03/07 HTML / CSS
Linux文件操作命令都有哪些
2016/07/23 面试题
大学应届生求职简历的自我评价
2013/10/08 职场文书
教师绩效工资方案
2014/02/01 职场文书
幼儿园中班区域活动总结
2014/07/09 职场文书
家长高考寄语
2015/02/27 职场文书
教师求职信怎么写
2015/03/20 职场文书
2015年度房地产工作总结
2015/04/09 职场文书
2015年党总支工作总结
2015/05/25 职场文书
企业反腐倡廉心得体会
2015/08/15 职场文书
CSS变量实现主题切换的方法
2021/06/23 HTML / CSS
Java org.w3c.dom.Document 类方法引用报错
2021/08/07 Java/Android