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实现的Excel文件读写类
Jul 30 Python
Python 高级专用类方法的实例详解
Sep 11 Python
python保存数据到本地文件的方法
Jun 23 Python
Python开发的十个小贴士和技巧及长常犯错误
Sep 27 Python
Django中更改默认数据库为mysql的方法示例
Dec 05 Python
PyTorch 1.0 正式版已经发布了
Dec 13 Python
python用插值法绘制平滑曲线
Feb 19 Python
scrapy-redis源码分析之发送POST请求详解
May 15 Python
Python3之乱码\xe6\x97\xa0\xe6\xb3\x95处理方式
May 11 Python
python使用matplotlib:subplot绘制多个子图的示例
Sep 24 Python
python smtplib发送多个email联系人的实现
Oct 09 Python
python3定位并识别图片验证码实现自动登录功能
Jan 29 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
Laravel中如何增加自定义全局函数详解
2017/05/09 PHP
javascript 学习之旅 (1)
2009/02/05 Javascript
IE8 chrome中table隔行换色解决办法
2010/07/09 Javascript
jquery(live)中File input的change方法只起一次作用的解决办法
2011/10/21 Javascript
Underscore.js 的模板功能介绍与应用
2012/12/24 Javascript
NodeJS的url截取模块url-extract的使用实例
2013/11/18 NodeJs
JS判断是否360安全浏览器极速内核的方法
2015/01/29 Javascript
JavaScript数据结构与算法之栈与队列
2016/01/29 Javascript
HTML5canvas 绘制一个圆环形的进度表示实例
2016/12/16 Javascript
JavaScript获取短信验证码(周期性)
2016/12/29 Javascript
详解如何在Vue2中实现组件props双向绑定
2017/03/29 Javascript
Ajax验证用户名或昵称是否已被注册
2017/04/05 Javascript
IScroll那些事_当内容不足时下拉刷新的解决方法
2017/07/18 Javascript
Angular.js初始化之ng-app的自动绑定与手动绑定详解
2017/07/31 Javascript
详解require.js配置路径的用法和css的引入
2017/09/06 Javascript
JS动态添加元素及绑定事件造成程序重复执行解决
2017/12/07 Javascript
vue router嵌套路由在history模式下刷新无法渲染页面问题的解决方法
2018/01/25 Javascript
vue和webpack项目构建过程常用的npm命令详解
2018/06/15 Javascript
jQuery实现简单的Ajax调用功能示例
2019/02/15 jQuery
解决element ui select下拉框不回显数据问题的解决
2019/02/20 Javascript
深入探索VueJS Scoped CSS 实现原理
2019/09/23 Javascript
原生JavaScript实现五子棋游戏
2020/11/09 Javascript
python实现requests发送/上传多个文件的示例
2018/06/04 Python
详解Python中的type和object
2018/08/15 Python
对python制作自己的数据集实例讲解
2018/12/12 Python
Python使用configparser库读取配置文件
2020/02/22 Python
详解python中的异常捕获
2020/12/15 Python
元旦晚会上单位领导演讲稿
2014/01/05 职场文书
网吧最新创业计划书范文
2014/03/27 职场文书
公司任命书模板
2014/06/06 职场文书
奥巴马经典演讲稿
2014/09/13 职场文书
六查六看六改心得体会
2014/10/14 职场文书
小学生读书笔记范文
2015/06/30 职场文书
2016应届毕业生自荐信范文
2016/01/28 职场文书
golang为什么要统一错误处理
2022/04/03 Golang
MYSQL常用函数介绍
2022/05/05 MySQL