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中的pprint折腾记
Jan 21 Python
使用Python编写vim插件的简单示例
Apr 17 Python
Python内置函数OCT详解
Nov 09 Python
微信小程序跳一跳游戏 python脚本跳一跳刷高分技巧
Jan 04 Python
分享一下Python数据分析常用的8款工具
Apr 29 Python
python实现二维插值的三维显示
Dec 17 Python
浅谈python编译pyc工程--导包问题解决
Mar 20 Python
基于python进行抽样分布描述及实践详解
Sep 02 Python
Python : turtle色彩控制实例详解
Jan 19 Python
利用python清除移动硬盘中的临时文件
Oct 28 Python
Python实现文本文件拆分写入到多个文本文件的方法
Apr 18 Python
Flask搭建一个API服务器的步骤
May 28 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
php下用GD生成生成缩略图的两个选择和区别
2007/04/17 PHP
PHP开发中常用的8个小技巧
2008/08/27 PHP
PHP的SQL注入实现(测试代码安全不错)
2011/02/27 PHP
深入理解PHP几个算法:PHP冒泡、PHP二分法、PHP求素数、PHP乘法表
2013/06/06 PHP
PHP 进度条函数的简单实例
2017/09/19 PHP
详解php 使用Callable Closure强制指定回调类型
2017/10/26 PHP
YII框架模块化处理操作示例
2019/04/26 PHP
Yii框架常见缓存应用实例小结
2019/09/09 PHP
js小技巧--自动隐藏红叉叉
2007/08/13 Javascript
用javascript实现计算两个日期的间隔天数
2007/08/14 Javascript
jquery 图片上传按比例预览插件集合
2011/05/28 Javascript
js为数字添加逗号并格式化数字的代码
2013/08/23 Javascript
js showModalDialog 弹出对话框的简单实例(子窗体)
2014/01/07 Javascript
jQuery通过控制节点实现仅在前台通过get方法完成参数传递
2015/02/02 Javascript
jQuery实现返回顶部效果的方法
2015/05/29 Javascript
javascript实现3D切换焦点图
2015/10/16 Javascript
jQuery表格插件datatables用法汇总
2016/03/29 Javascript
JS判断输入的字符串是否是数字的方法(正则表达式)
2016/11/29 Javascript
jQuery联动日历的实例解析
2016/12/02 Javascript
electron-vue利用webpack打包实现多页面的入口文件问题
2019/05/12 Javascript
VUEX 数据持久化,刷新后重新获取的例子
2019/11/12 Javascript
用Golang运行JavaScript的实现示例
2019/11/25 Javascript
解决angular 使用原生拖拽页面卡顿及表单控件输入延迟问题
2020/04/21 Javascript
[01:10]为家乡而战!完美世界城市挑战赛全国总决赛花絮
2019/07/25 DOTA
python 2.6.6升级到python 2.7.x版本的方法
2016/10/09 Python
django上传图片并生成缩略图方法示例
2017/12/11 Python
python3转换code128条形码的方法
2019/04/17 Python
python实现简单聊天室功能 可以私聊
2019/07/12 Python
python3实现微型的web服务器
2019/09/03 Python
css3强大的动画效果animate使用说明及浏览器兼容介绍
2013/01/09 HTML / CSS
一套Delphi的笔试题一
2016/02/14 面试题
志愿者宣传口号
2014/06/17 职场文书
中秋节活动总结
2014/08/29 职场文书
银行求职自荐信范文
2015/03/04 职场文书
2015年“公民道德宣传日”活动方案
2015/05/06 职场文书
pytorch中的numel函数用法说明
2021/05/13 Python