您的位置:首页 > 编程语言 > Go语言

django-celery动态添加定时任务

2015-08-14 17:00 881 查看
为了使用celery替代crontab并做到实时添加定时任务的效果,需要使用django-celery,效果如下图,来自:https://www.caktusgroup.com/blog/2014/06/23/scheduling-tasks-celery/要使用django-celery,需要安装python的以下包:django,celery,django-celery。其中django安装比较麻烦,首先它和python版本相关,django1.7.9和1.8.3都是支持python2.7,所以打算安装这俩版本中其一;其次,django需要sqlite2或sqlite3的支持,而python2.7中sqlite是已经包含在内、却没有编译的,因此先要安装sqlite、然后重新安装python2.7。整个开发环境安装和配置如下一.环境配置首先安装sqlite3。先手动从http://www.sqlite.org/download.html下载软件包sqlite-autoconf-3081002.tar.gz(也可直接使用wget),tar-xzvfsqlite-autoconf-3081002.tar.gz./configure--prefix=/home/panxiaofeng/install#指定安装目录makemakeinstall接着重新安装python2.7,首先,tar-zxfPython-2.7.9.tgzcd~/tools/Python-2.7.9修改setup.py,sqlite_inc_paths=['/usr/include','/usr/include/sqlite','/usr/include/sqlite3','/usr/local/include','/usr/local/include/sqlite','/usr/local/include/sqlite3','~/install/include','~/install/include/sqlite3',]也就是加上sqlite3的路径,然后,./configure--prefix=/home/panxiaofeng/install/&&make-j4&&makeinstall#4核并行编译ln-s~/install/bin/python~/bin/python完成。测试安装成功否,~/bin/python>>>importsqlite3>>>没报错即可。配置python开发环境,不仅要安装python2.7,还需要安装一些工具,unzipsetuptools-12.0.3.zip2>/dev/null1>/dev/nullcd~/tools/setuptools-12.0.3~/bin/pythonsetup.pybuildinstall#用哪个python来安装包,包就会安装到哪个python的目录下,其他版本的python无法使用该包tar-zxfpip-6.0.6.tar.gzcd~/tools/pip-6.0.6~/bin/pythonsetup.pybuildinstallln-s~/install/bin/pip~/bin/pip其中pip工具用来安装python包很方便,但是默认的源下载很慢经常超时,需要采用一些方法加速,具体可以参考/article/1735661.html。本文安装加速的手段采用了最简单的换源:sudo~/bin/pipinstall-ihttp://pypi.douban.com/simpledjango,用豆瓣的源,暴快!注意,用哪个python安装的pip工具来安装包,包就会安装到哪个python的目录下,其他版本的python无法使用该包。然后,celery和django-celery也采用该源来安装,快得很,不像之前用默认的源,很卡很慢还经常失败。记录一下,之前也手动安装过Django,手动安装唯一需要注意的一点就是sudopythonsetup.py时搞清楚你用的是哪个版本的python,不要搞错版本了,如果默认的python不能用,就自己指定,比如,sudo/usr/local/bin/python2.7setup.pyinstall。还有手动安装比较利于卸载包:setup.py帮助你纪录安装细节方便你卸载pythonsetup.pyinstall--recordlog这时所有的安装细节都写到log里了想要卸载的时候catlog|xargsrm-rf就可以干净卸载了最后记录下解决pip下载失败的一些可能有用的方法:出现Badmd5hashforpackagehttps://here/package/path错误时,尝试,1.Youcanusewheelsforinstallingpythonpackages:pipinstallwheel.Thentrytopipthepackageyouwantagain.2.pip失败后,会看到你需要的包信息,那么直接下载安装,如下面的例子,[root@masterconn]#pipinstallchardet==2.2.1Collectingchardet==2.2.1/usr/lib/python2.6/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:79:InsecurePlatformWarning:AtrueSSLContextobjectisnotavailable.Thispreventsurllib3fromconfiguringSSLappropriatelyandmaycausecertainSSLconnectionstofail.Formoreinformation,seehttps://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.InsecurePlatformWarningDownloadingchardet-2.2.1-py2.py3-none-any.whl(180kB)72%|███████████████████████▎|131kB357bytes/seta0:02:19Hashofthepackagehttps://pypi.python.org/packages/2.7/c/chardet/chardet-2.2.1-py2.py3-none-any.whl#md5=556de73cc5d2d14233b3512798423da1(fromhttps://pypi.python.org/simple/chardet/)(be001cd2dbe90bb1f1dd4ab4b008c169)doesn'tmatchtheexpectedhash556de73cc5d2d14233b3512798423da1!Badmd5hashforpackagehttps://pypi.python.org/packages/2.7/c/chardet/chardet-2.2.1-py2.py3-none-any.whl#md5=556de73cc5d2d14233b3512798423da1(fromhttps://pypi.python.org/simple/chardet/)解决方案:wgethttps://pypi.python.org/packages/2.7/c/chardet/chardet-2.2.1-py2.py3-none-any.whl--no-check-certificatemd5sumchardet-2.2.1-py2.py3-none-any.whlpipinstallchardet-2.2.1-py2.py3-none-any.whl二.简单测试django~/bin/python~/install/bin/django-admin.pystartprojectdjtest#使用上面安装了相关包的pythoncddjtest~/bin/pythonmanage.pyrunserver0.0.0.0:10501然后最便打开一个浏览器输入:10.121.84.90:10501/,得到响应的页面就成功了。10.121.84.90是执行上面命令的主机ip,即djangoserver的主机ip。~/bin/pythonmanage.pycreatesuperuser来创建admin账户。三.django-celery替代crontab测试:测试djcelery的动态添加任务函数并crontab部署的功能。目录结构,djtest├──apps│├──app1││├──__init__.py││└──tasks.py│├──app2││├──__init__.py││└──tasks.py│├──__init__.py│├──tasks.py├──djtest│├──celery.py│├──__init__.py│├──settings.py│├──urls.py│├──wsgi.py└──manage.py其中manage.py、__init__.py、settings.py、urls.py、wsgi.py是~/bin/python~/install/bin/django-admin.pystartprojectdjtest命令自动生成的,但部分文件需要修改。首先修改settings.py,在里面添加,
[code]importdjcelery
###
djcelery.setup_loader()
###
CELERY_TIMEZONE='Asia/Shanghai'#并没有北京时区,与下面TIME_ZONE应该一致
BROKER_URL='redis://10.121.84.90:16379/8'
#任何可用的redis都可以,不一定要在djangoserver运行的主机上
CELERYBEAT_SCHEDULER='djcelery.schedulers.DatabaseScheduler'
###
#Applicationdefinition
INSTALLED_APPS=(
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'djcelery',
###
'apps',
###
)
TIME_ZONE='Asia/Shanghai'
###[/code]上面代码首先导出djcelery模块,并调用setup_loader方法加载有关配置;注意配置时区,不然默认使用UTC时间会比东八区慢8个小时。其中INSTALLED_APPS末尾添加两项,分别表示添加celery服务和自己定义的apps服务。接下来编写celery.py文件,
[code]#!/bin/python
from__future__importabsolute_import
importos
fromceleryimportCelery
os.environ.setdefault('DJANGO_SETTINGS_MODULE','djtest.settings')
#SpecifyingthesettingsheremeansthecelerycommandlineprogramwillknowwhereyourDjangoprojectis.#Thisstatementmustalwaysappearbeforetheappinstanceiscreated,whichiswhatwedonext:
fromdjango.confimportsettings
app=Celery('djtest')
app.config_from_object('django.conf:settings')
#Thismeansthatyoudon’thavetousemultipleconfigurationfiles,andinsteadconfigureCelerydirectlyfromtheDjangosettings.
#Youcanpasstheobjectdirectlyhere,butusingastringisbettersincethentheworkerdoesn’thavetoserializetheobject.
app.autodiscover_tasks(lambda:settings.INSTALLED_APPS)
#WiththelineaboveCelerywillautomaticallydiscovertasksinreusableappsifyoudefinealltasksinaseparatetasks.pymodule.#Thetasks.pyshouldbeindirwhichisaddedtoINSTALLED_APPinsettings.py.#SoyoudonothavetomanuallyaddtheindividualmodulestotheCELERY_IMPORTinsettings.py.
@app.task(bind=True)
defdebug_task(self):
print('Request:{0!r}'.format(self.request))#
dumpsitsownrequestinformation[/code]然后修改djtest/__init__.py,
[code]#!/bin/python
from__future__importabsolute_import
#Thiswillmakesuretheappisalwaysimportedwhen
#Djangostartssothatshared_taskwillusethisapp.
from.celeryimportappascelery_app
[/code]接下来编写你希望django去完成的app,本文中要编写的就是在INSTALLED_APPS中注册的apps。在celery.py中设定了对settings.py中INSTALLED_APPS做autodiscover_tasks,本文希望apps中能够接受这样的目录组织:所有的app都可以放到apps下面,而且每个app都有独立的目录,就和上面的app1、app2一样,每个app各自有各自的__init__.py和tasks.py(注意,每个app都需要__init__.py文件,可以是空白的)。但是这样的结构组织在启动时会报错说moduleapps找不到。然后在apps下增加了一个__init__.py文件,这时报错没了,但是apps下每个app的tasks.py中的任务函数还是无法被django和celeryworker找到。然后尝试了在apps下面写一个__init__.py(空白)和task.py,所有的taskfunction都写到tasks.py中,如下,
[code]from__future__importabsolute_import
fromceleryimporttask
fromceleryimportshared_task
#fromcelery.taskimporttasks
#fromcelery.taskimportTask
@task()
#@shared_task
defadd(x,y):
print"%d+%d=%d"%(x,y,x+y)
returnx+y
#classAddClass(Task):
#defrun(x,y):
#print"%d+%d=%d"%(x,y,x+y)
#returnx+y
#tasks.register(AddClass)
@shared_task
defmul(x,y):
print"%d*%d=%d"%(x,y,x*y)
returnx*y
@shared_task
defsub(x,y):
print"%d-%d=%d"%(x,y,x-y)
returnx-y
[/code]上面代码中,decorator可以用@task()也可以用@shared_task。然后,同步django数据库,~/bin/pythonmanage.pymakemigrations~/bin/pythonmanage.pymigrate运行djangowebserver和celerybeat、celeryworker,~/bin/pythonmanage.pycreatesuperuser#如果没管理员账号,先建一个~/bin/pythonmanage.pyrunserver0.0.0.0:10501#重新指定server使用的端口,0.0.0.0即运行该命令的主机ip:10.121.84.90~/bin/pythonmanage.pycelerybeat~/bin/pythonmanage.pyceleryworker-c6-ldebug#正式上线-l改成info~/bin/celeryflower--port=10201--broker=redis://10.121.84.90:16379/8#如果必要开个flower,从而通过网页10.121.84.90:10201来监控任务情况这时候,在浏览器输入10.121.84.90:10501/admin/可以登录管理员界面,在该界面可以点Periodictasks来添加crontab任务,点击右上角的Addperiodictask,在该页面上可以通过上图中的Task(registered)或者Task(custom)来增加crontab任务,前者会在下拉列表中显示apps/tasks.py里定义的taskfunction。除了动态增加、删除任务,上面的Enabled选项可以用来暂停任务。这时,在djangowebserver和celerybeat、celeryworker都启动着的情况下,修改apps/tasks.py文件,往其中增减taskfunction,会实时反映在上图中Task(registered)的下拉列表上,验证了django的autodiscover_tasks功能的确实有效的。但是在不重启celeryworker的情况下,添加该taskfunction的crontab任务,celeryworker无法调用该taskfunction,从而报错,也就是说django的确可以autodiscover_tasks,并且celerybeat也会准确无误的发布动态增加的任务消息,只是celeryworker无法动态调用新的taskfunction(这是不是说django的autodiscover_tasks功能只管django而不管celeryworker,从而只能看到而无法执行,其实并没有什么卵用?)。。。然后验证Task(custom)选项的作用:我自己写一个app3,放在apps同级目录和apps的子目录各自试验,不过不往settings.py中INSTALLED_TASKS中注册子目录(注册了不就是Task(registered)了吗),然后Addperiodictask时在Task(custom)填写"apps.app3.tasks.function"或者"app3.tasks.function"或者"目录的绝对路径.tasks.function",celerybeat会发布相关的任务消息,但是celeryworker无法调用function(这就表示,想绕开INSTALLED_APPS和celery_worker是不行的,Task(custom)似乎没有什么卵用)。。。总的来说,就是Task(registered)或者Task(custom)并不能做到动态添加taskfunction并部署该taskfunction的相关任务,虽然官方文档似乎表示可以。现在能做到的就是,需要的taskfunction写好,然后通过上面的例子可以动态的部署写好的taskfunction的crontab任务(只可以动态传参数),似乎有点寒碜,不过勉强算是够用了,只要把taskfunction写得通用一点,比如说开一个进程去调用用户自己编写的脚本。上面提到的失败的尝试,今后有空再去探索吧。后续:(一)经过实验,celeryworker使用--autoreload选项启动可以解决上面apps/tasks.py中新增、减函数无法被worker检测到的问题,具体见http://docs.celeryproject.org/en/latest/userguide/workers.html#autoreloading。不过该功能属于实验阶段,官方不推荐使用,而且,Whenrunningtheworkerwiththe
--autoreload
option,itcorrectlyreloadsifthe
tasks.py
moduleischanged.ButifIchangeamodulethatisimportedandusedbythetasksmodule,nothingisreloadedandtheworkerstillusestheoldcode.Currentlyautoreloaderdoesn'tmonitorthedependencytree.来源:<https://github.com/celery/celery/issues/1025>不过,用来实现apps/tasks.py里面增减函数、自动被worker检测,是足够了的。在原生的celery中,--autoreload要搭配CELERY_IMPORT或include设定来用(autoreload这两项设定中的module文件),不过在django中,INSTALLED_APPS设定可以用来替代CELERY_IMPORT,所以django中可以使celeryworkerautoreloadINSTALLED_APPS设定目录下的tasks.py文件。最终,django的autodiscover_tasks用来使djangoadmin页面动态检测apps/tasks.py里taskfunction的变化,celery的autoreload用来使celeryworker动态检测apps/tasks.py里taskfunction的变化,从而实现动态增减taskfunction并正确执行的功能。注意,动态新增的taskfunction的decorator使用@tasks会报"NotRegistered:u'apps.tasks.funcName'"的错误,用@shared_task才可以正确被worker执行。不少问题出在celeryworker上,建议看看WorkersGuide:http://docs.celeryproject.org/en/latest/userguide/workers.html#autoreloading。之前的尝试中,apps下每个app一个子目录,如果只在INSTALLED_APPS中添加"apps"这一项而不注册子目录,django无法autodiscover_tasks到子目录(google出来的例子是每个子目录都注册),看来autodiscover_tasks只支持注册目录下tasks.py文件的监测、而不支持注册目录下子目录的tasks.py文件的监测(如果支持,那么celeryworker的--autoreload也可以检测到子目录的tasks.py文件,那么apps子目录支持的问题就解决了)。不过,要实现apps下每个app一个子目录,可以考虑换种途径:添加新的子目录时动态修改settings.py中的INSTALLED_APPS,因为django似乎有支持这种操作的函数(参考笔记./【转】python-dynamicallyloadingdjangoappsatruntime,每增加一次调用一次用来增加的代码)。实验等有空再做。综上所述,现在动态修改apps/tasks.py、添加apps目录下新的任务子目录(子目录目录结构和最上面app1、app2一样)的功能都可以了。唯一没有起作用的就是djangoadmin页面上的Task(custom)选项,该选项设计的初衷想必是:你写一个类似app1的app,直接通过该选项就可以调用。想法很好,只是目前来看,这个设计绕过了INSTALLED_APPS设定,似乎并不能被django和celeryworker检测到新增的app,起不了作用。(二)django正式上线后,需要将settings.py中的"DEBUG=True"注释掉免得内存泄漏,同时在下面加上"ALLOWED_HOSTS=['10.121.84.90']",使得admin网页可用(10.121.84.90是djangoserver运行的主机ip)。这时候admin网页会比较丑陋而且有些功能不正常,因为非debug模式下djangoserver不会帮忙处理静态文件,解决方案是在启动djangoserver的时候添加--insecure选项(http://stackoverflow.com/questions/5836674/why-does-debug-false-setting-make-my-django-static-files-access-fail),使用该选项要确定settings.py的INSTALLED_APPS中有'django.contrib.staticfiles'。参考:FirststepswithDjango:http://celery.readthedocs.org/en/latest/django/first-steps-with-django.htmlDjango+Celery实现周期任务:http://m.blog.csdn.net/blog/zhenxuhit/44302809djcelery入门:实现运行定时任务!!!:/article/3462927.htmlDjango中如何使用django-celery完成异步任务(1):http://www.weiguda.com/blog/73/Configuringthewebserverandworker:http://www.syncano.com/configuring-running-django-celery-docker-containers-pt-1/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: