Django中异步任务celery
一、什么是 Celery
官网描述如下:Celery是一个基于消息传递的异步任务队列。关注与时时操作,但是也支持任务调度。执行单元称之为Task,被一个或多个worker处理。该woker是使用multiprocessing, Eventlet, or gevent。Task能够被异步(后台)执行也可以被同步(等待完成)执行。Celery在生产服务器每天处理数以万计的Task。访问官网查看更多信息。
可见Celery主要是用来处理异步任务和定时任务的。但我们为什么需要异步任务。或者定时任务呢?
1.1 假设用户发起一个request,并等待request返回。但是在request 经过django一层层处理到达我们的view时,view要进行一次比较耗时的操作,比如分析日志文件或者发起一个http请求。这些操作耗时无法确定,这样我们的API接口的响应时间就无法预知了。为了给用户以良好的体验,我们应该把这种耗时的任务放在后台处理。
1.2 定时任务,之前遇到一个需求是这样的。根据Django的日志数据分析用户的访问习惯和页面访问次数。这种任务不可能在用的时候再去分析统计,那肯定来不及了。所以需要在每天零点整自动分析这些日志。当遇到这种问题时我们可以采用Celery的定时任务。当然还有另一种解决方案,自定义Django Command 然后用shell脚本执行。然后添加到系统定时任(crontab)务里。
二、本文所依赖的库
Django (1.7.4) Celery (3.1.17) django-celery (3.1.16)
三、依赖库的安装
这里推荐vietualenv+virtualenvwarpper管理依赖环境,真心很方便
pip install django==1.7.4 pip install celery==3.1.17 pip install django-celery==3.1.16
四、开始编码
django-admin.py startproject celery_tutorial cd celery_tutorial/ django-admin.py startapp main
五、django设置
我们暂时使用django runserver来启动celery. 而Celery代理人(broker), 我们使用Django database broker implementation. 现在我们只需要知道Celery需要broker, 使用django自身便可以充当broker. (但在部署时, 我们最好使用更稳定和高效的broker, 例如Redis.)
在settings.py中:
import djcelery djcelery.setup_loader() BROKER_URL = 'django://' ... INSTALLED_APPS = ( ... 'djcelery', 'kombu.transport.django', ... )
第一二项是必须的, 第三项则告诉Celery使用Django项目作为broker.
在INSTALLED_APPS中添加的djcelery是必须的. kombu.transport.django则是基于Django的broker
最后创建Celery所需的数据表, 运行:
python manage.py migrate
六、配置View 以及 URL
新建并配置celery_tutorial/celery_tutorial/tasks.py:
# encoding:utf-8 import time from celery.task import task @task def _do_kground_work(name): """ 这里用time 模拟耗时操作 """ for i in range(1,10): print 'hello:%s %s'%(name,i) time.sleep(1)
配置celery_tutorial/celery_tutorial/views.py:
# encoding:utf-8 from django.http import HttpResponse from django.views.generic import View from main.tasks import _do_kground_work class Hello(View): def get(self, request, *args, **kwargs): _do_kground_work.delay('GreenPine') return HttpResponse('Hello, World!')
配置celery_tutorial/celery_tutorial/urls.py:
from django.conf.urls import patterns, include, url from django.contrib import admin from main.views import Hello urlpatterns = patterns('', url(r'^$',Hello.as_view()), url(r'^admin/', include(admin.site.urls)), )
运行项目:
./manage.py runserver
七、测试
a、启动woker
正如之前说到的, 我们需要worker来执行task. 以下是在开发环境中的如何启动worker:
首先启动terminal, 如同开发django项目一样, 激活virtualenv, 切换到django项目目录. 然后启动django自带web服务器: python manage.py runserver.
python manage.py celery worker --loglevel=info
再生产环境中可以用如下命令启动woker,并用supervisor监控:
nohup python manage.py celery worker —loglevel=info&
b、测试task
在浏览器里输入http://localhost:8000/
此时请求会非常迅速,瞬间就返回了而不是等待_do_kground_work的执行。
如果你观察woker终端 你会发现有这样的输出:
[2015-02-11 16:13:25,490: INFO/MainProcess] Received task: main.tasks._do_kground_work[001a4d4b-4d9e-4a70-b2b4-876aca30a010] [2015-02-11 16:13:25,493: WARNING/Worker-1] hello:GreenPine 1 [2015-02-11 16:13:26,494: WARNING/Worker-1] hello:GreenPine 2 [2015-02-11 16:13:27,496: WARNING/Worker-1] hello:GreenPine 3 [2015-02-11 16:13:28,498: WARNING/Worker-1] hello:GreenPine 4 [2015-02-11 16:13:29,499: WARNING/Worker-1] hello:GreenPine 5 [2015-02-11 16:13:30,499: WARNING/Worker-1] hello:GreenPine 6 [2015-02-11 16:13:31,501: WARNING/Worker-1] hello:GreenPine 7 [2015-02-11 16:13:32,502: WARNING/Worker-1] hello:GreenPine 8 [2015-02-11 16:13:33,503: WARNING/Worker-1] hello:GreenPine 9 [2015-02-11 16:13:34,527: INFO/MainProcess] Task main.tasks._do_kground_work[001a4d4b-4d9e-4a70-b2b4-876aca30a010] succeeded in 9.034652657s: None