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操作ssh实现服务器日志下载的方法
Jun 03 Python
Flask框架的学习指南之用户登录管理
Nov 20 Python
Python实现的多线程同步与互斥锁功能示例
Nov 30 Python
python3安装pip3(install pip3 for python 3.x)
Apr 03 Python
对python生成业务报表的实例详解
Feb 03 Python
python使用threading.Condition交替打印两个字符
May 07 Python
python多线程与多进程及其区别详解
Aug 08 Python
详解Python Opencv和PIL读取图像文件的差别
Dec 27 Python
python 在threading中如何处理主进程和子线程的关系
Apr 25 Python
使用python修改文件并立即写回到原始位置操作(inplace读写)
Jun 28 Python
利用python汇总统计多张Excel
Sep 22 Python
关于python pygame游戏进行声音添加的技巧
Oct 24 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
索尼SONY SRF-S83/84电路分析和打磨
2021/03/02 无线电
基于在生产环境中使用php性能测试工具xhprof的详解
2013/06/03 PHP
PHP实现的博客欢迎提示功能(很特别哦)
2014/06/05 PHP
php出现内存位置访问无效错误问题解决方法
2014/08/16 PHP
PHP获取音频文件的相关信息
2015/06/22 PHP
php魔法函数与魔法常量使用介绍
2017/07/23 PHP
添加到收藏夹代码(兼容几乎所有的浏览器)
2007/01/09 Javascript
Discuz! 6.1_jQuery兼容问题
2008/09/23 Javascript
JQuery 常用方法基础教程
2009/02/06 Javascript
JavaScript 设计模式 富有表现力的Javascript(一)
2010/05/26 Javascript
node.js操作mongodb学习小结
2015/04/25 Javascript
Javascript中JSON数据分组优化实践及JS操作JSON总结
2017/12/22 Javascript
react-native-video实现视频全屏播放的方法
2018/03/19 Javascript
vue cli升级webapck4总结
2018/04/04 Javascript
解决bootstrap中下拉菜单点击后不关闭的问题
2018/08/10 Javascript
React 项目迁移 Webpack Babel7的实现
2018/09/12 Javascript
Jquery遍历筛选数组的几种方法和遍历解析json对象,Map()方法详解以及数组中查询某值是否存在
2019/01/18 jQuery
微信小程序的开发范式BeautyWe.js入门详解
2019/07/10 Javascript
java和js实现的洗牌小程序
2019/09/30 Javascript
angular8和ngrx8结合使用的步骤介绍
2019/12/01 Javascript
JS Thunk 函数的含义和用法实例总结
2020/04/08 Javascript
pip 错误unused-command-line-argument-hard-error-in-future解决办法
2014/06/01 Python
python列出目录下指定文件与子目录的方法
2015/07/03 Python
Python实现购物评论文本情感分析操作【基于中文文本挖掘库snownlp】
2018/08/07 Python
Python3爬虫学习之将爬取的信息保存到本地的方法详解
2018/12/12 Python
Python时间序列缺失值的处理方法(日期缺失填充)
2019/08/11 Python
HTML5 placeholder(空白提示)属性介绍
2013/08/07 HTML / CSS
linux面试相关问题
2013/04/28 面试题
英语专业毕业生自我鉴定
2013/11/09 职场文书
采购部部长岗位职责
2014/02/06 职场文书
二手房购房协议书范本
2014/10/05 职场文书
2014年监理个人工作总结
2014/12/11 职场文书
销售员自我评价
2015/03/11 职场文书
《祁黄羊》教学反思
2016/02/20 职场文书
关于Python中*args和**kwargs的深入理解
2021/08/07 Python
python中Pyqt5使用Qlabel标签播放视频
2022/04/22 Python