1.定義:
? ? Celery是一個(gè)異步的任務(wù)隊(duì)列(也叫做分布式任務(wù)隊(duì)列)
2.工作結(jié)構(gòu)
? ? Celery分為3個(gè)部分
? ? (1)worker部分負(fù)責(zé)任務(wù)的處理,即工作進(jìn)程(我的理解工作進(jìn)程就是你寫(xiě)的python代碼,當(dāng)然還包括python調(diào)用系統(tǒng)工具功能)
? ? (2)broker部分負(fù)責(zé)任務(wù)消息的分發(fā)以及任務(wù)結(jié)果的存儲(chǔ),這部分任務(wù)主要由中間數(shù)據(jù)存儲(chǔ)系統(tǒng)完成,比如消息隊(duì)列服務(wù)器RabbitMQ、redis、
Amazon SQS、MongoDB、IronMQ等或者關(guān)系型數(shù)據(jù)庫(kù),使用關(guān)系型數(shù)據(jù)庫(kù)依賴(lài)sqlalchemy或者django的ORM
? ? (3)Celery主類(lèi),進(jìn)行任務(wù)最開(kāi)始的指派與執(zhí)行控制,他可以是單獨(dú)的python腳本,也可以和其他程序結(jié)合,應(yīng)用到django或者flask等web框架里面以及你能想到的任何應(yīng)用
3.話(huà)不多說(shuō),用起來(lái)
? ? (1)安裝Celery(要安裝celery3版本,4版本改動(dòng)較大沒(méi)測(cè)試)
#pip install celery==3.1.17
? ? (2)broker部分此處使用安裝好的redis服務(wù)6380端口的db0作為消息隊(duì)列,普通redis服務(wù)的安裝此處不做介紹
? (3)Celery的使用一(單獨(dú)腳本調(diào)用,簡(jiǎn)單方便)
? ? ? ? #注:不考慮任務(wù)的結(jié)果存儲(chǔ)情況
? ? ? ? ?<1>/tmp/tasks.py(實(shí)際腳本中不要寫(xiě)中文注釋?zhuān)?/p>
#!/usr/bin/env python
# -*- coding=utf-8 -*-
from celery import Celery
from celery import platforms
#用于開(kāi)啟root也可以啟動(dòng)celery服務(wù),默認(rèn)是不允許root啟動(dòng)celery的
platforms.C_FORCE_ROOT = True
#創(chuàng)建一個(gè)celery實(shí)例,傳遞進(jìn)去的第一個(gè)參數(shù)tasks必須是本文件的文件名tasks,指定broker為本機(jī)redis6380服務(wù)
celery = Celery('tasks', broker='redis://localhost:6380/0')
#使用celery實(shí)例的task裝飾器裝飾add函數(shù),此處的add函數(shù)可以當(dāng)作后期的耗時(shí)任務(wù)對(duì)待
@celery.task
def add(x,y):
? ? return x + y
? ? ? ?<2>啟動(dòng)celery服務(wù)
#cd /tmp
#celery -A tasks worker --loglevel=info
? ? ? ?<3>驗(yàn)證執(zhí)行任務(wù)
? ? ? ?#導(dǎo)入模塊,執(zhí)行add函數(shù),此處使用add.delay(3,4)而不是add(3,4),因?yàn)楸籧elery封裝了,要異步執(zhí)行需要額外使用add.delay(3,4)
? ?? ? #需要注意,如果把返回值賦值給一個(gè)變量,那么原來(lái)的應(yīng)用程序也會(huì)被阻塞,需要等待異步任務(wù)返回的結(jié)果。因此,實(shí)際使用中,不需要把結(jié)果賦值。
#cd /tmp
#python
>>>from tasks import add
>>>add.delay(3,4)
? ? ? ?#celery服務(wù)的窗口會(huì)刷出任務(wù)的信息,以及是否處理成功,以及結(jié)果
? ? ? ?#將來(lái)只要在別的程序中引入tasks中的add函數(shù),就是異步的了,是不是有點(diǎn)屌。。。。。
? ? ? ?<4>擴(kuò)展知識(shí),指定隊(duì)列名
? ? ? ?傳入redis中的指定隊(duì)列testq怎么玩?(其他broker引擎也支持)
? ? ? ?啟動(dòng)celery服務(wù)的時(shí)候添加額外參數(shù)-Q '隊(duì)列名'
#cd /tmp
#celery -A tasks.tasks worker --loglevel=info -Q 'testq'
? ? ? ?跑任務(wù)的時(shí)候指定testq隊(duì)列名
#cd /tmp
#python
>>>from tasks import add
>>>add.delay(3,4,queue='testq')
? ? ? ?<5>擴(kuò)展知識(shí),指定開(kāi)啟的worker進(jìn)程數(shù)(底層是調(diào)用的Python的multiprocessing模塊中的Pool進(jìn)程池思想來(lái)做)
? ? ? ?-c 5 開(kāi)啟5個(gè)worker進(jìn)程來(lái)同時(shí)搶任務(wù),跑任務(wù)
#cd /tmp
#celery -A tasks.tasks worker --loglevel=info -c 5
? ? ? ?<6>擴(kuò)展知識(shí),管理broker里面的數(shù)據(jù),查看任務(wù)狀態(tài),以及任務(wù)的詳細(xì)信息
? ? ? ?安裝一個(gè)叫flower的webui,提供任務(wù)查詢(xún),worker生命管理,以及路由管理等(底層是通過(guò)tornado框架封裝的)
#pip install flower
#任意目錄下執(zhí)行都可以
#celery flower --port=5555 --broker=redis://localhost:6380/0
? ? ? ?#里面可以看到任務(wù)參數(shù),結(jié)果,接受任務(wù)時(shí)間,任務(wù)開(kāi)始時(shí)間,任務(wù)狀態(tài),Started是任務(wù)進(jìn)行中,Success是任務(wù)跑完執(zhí)行成功
? ? (4)Celery的使用二(項(xiàng)目方式,也叫做Python包方式,結(jié)構(gòu)清晰,低耦合;相比純腳本方式略復(fù)雜,用不用由你)
? ? 創(chuàng)建一個(gè)叫做proj的Python包(創(chuàng)建Python包的操作此處不做詳細(xì)說(shuō)明,tree /tmp/proj)
? ? ? ?<1>proj/celery.py
? ? ? #from __future__ import absolute_import據(jù)說(shuō)添加此行可以低降低出錯(cuò)的概率哦(阿門(mén)保佑;其實(shí)就是兼容Python版本的一個(gè)東東)
? ? ? #創(chuàng)建一個(gè)celery的實(shí)例,名字叫做app,傳遞進(jìn)去的第一個(gè)參數(shù)是Python包的名字,include加載任務(wù)文件,config_from_object指定celery的配置文件(好吧,看起來(lái)比單純使用腳本方式麻煩點(diǎn),請(qǐng)繼續(xù)往下看)
#!/usr/bin/env python
# -*- coding=utf-8 -*-
from __future__ import absolute_import
from celery import Celery
app = Celery('proj', include=['proj.tasks'])
app.config_from_object('proj.config')
if __name__ == '__main__':
? ? app.start()
? ? ? ?<2>proj/config.py
? ? ? ?#配置文件里指定broker
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import absolute_import
BROKER_URL = 'redis://127.0.0.1:6380/0'
? ? ? ?<3>proj/tasks.py
? ? ? ?#導(dǎo)入celery實(shí)例,實(shí)例綁定任務(wù)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import absolute_import
from proj.celery import app
@app.task
def add(x, y):
? ? return x + y
? ? ? ?<4>開(kāi)啟celery服務(wù)(特定目錄指定包名字啟動(dòng))
#cd /tmp/
#celery -A proj worker -l info
? ? ? ?<5>擴(kuò)展功能,指定隊(duì)列名,調(diào)整worker進(jìn)程數(shù),頁(yè)面管理celery同上,不再做說(shuō)明
?(5)Celery的使用三(django-celery模式;#反正我喜歡用這種)
? ? ? ?django調(diào)用celery跑異步任務(wù),常見(jiàn)場(chǎng)景有注冊(cè)成功,發(fā)送郵件可以異步來(lái)防止網(wǎng)絡(luò)IO阻塞,以及耗時(shí)間的任務(wù),例如需要去跑9000臺(tái)IP的某些配置參數(shù)任務(wù),或者下發(fā)任務(wù)執(zhí)行,可能需要10幾分鐘才能跑完,就可以WEB應(yīng)用中使用這種異步方式
? ? ? ?<1>安裝django-celery軟件包
? ? ? ?#一定要注意celery的版本和django-celery的小版本要保持一致,否則會(huì)有各種雜七雜八的小問(wèn)題(都是淚.......)
#pip install celery==3.1.17
#pip install django-celery==3.1.17
? ? ? ?<2>創(chuàng)建celery必須的數(shù)據(jù)庫(kù)表結(jié)構(gòu)
#cd Python_20161203
#python manage.py migrate
? ? ? ?<3>django項(xiàng)目的settings.py文件中追加如下內(nèi)容;app呢是django項(xiàng)目里面的應(yīng)用名字
? ? ? ?settings.py
import djcelery
djcelery.setup_loader()
BROKER_URL = 'redis://127.0.0.1:6380/0'
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6380/1'
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_IMPORTS = ('app.tasks', )
CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler'
CELERYD_CONCURRENCY = 20
參數(shù)說(shuō)明(可以根據(jù)自己的需求添加自己的參數(shù)):
CELERY_RESULT_BACKEND = "redis://127.0.0.1:6380/1'" #結(jié)果存儲(chǔ)
CELERY_TASK_RESULT_EXPIRES = 1200 # celery任務(wù)執(zhí)行結(jié)果的超時(shí)時(shí)間,我的任務(wù)都不需要返回結(jié)果,只需要正確執(zhí)行就行
CELERYD_CONCURRENCY = 20 # celery worker的并發(fā)數(shù) 也是命令行-c指定的數(shù)目,事實(shí)上實(shí)踐發(fā)現(xiàn)并不是worker也多越好,保證任務(wù)不堆積,加上一定新增任務(wù)的預(yù)留就可以
CELERYD_PREFETCH_MULTIPLIER = 4 # celery worker 每次去redis取任務(wù)的數(shù)量,我這里預(yù)取了4個(gè)慢慢執(zhí)行,因?yàn)槿蝿?wù)有長(zhǎng)有短沒(méi)有預(yù)取太多
CELERYD_MAX_TASKS_PER_CHILD = 200 # 每個(gè)worker執(zhí)行了多少任務(wù)就會(huì)死掉,我建議數(shù)量可以大一些,比如200
CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler' # 這是使用了django-celery默認(rèn)的數(shù)據(jù)庫(kù)調(diào)度模型,任務(wù)執(zhí)行周期都被存在你指定的orm數(shù)據(jù)庫(kù)中
? ? ? ?<4>app/tasks.py(在django的app應(yīng)用目錄下創(chuàng)建tasks.py任務(wù)文件,里面調(diào)用復(fù)雜的任務(wù)函數(shù))
#!/usr/bin/env python
# -*- coding=utf-8 -*-
###############################
from __future__ import absolute_import
from celery import task
import time
#task裝飾器封裝了celery函數(shù),為耗時(shí)的操作
@task
def add(x,y):
? ? for i in range(30):
? ? ? ?print i
? ? ? ?time.sleep(1)
? ? return x + y
? ? ? ?<5>添加驗(yàn)證功能,查看實(shí)際效果
? ? ? ?app/urls.py
urlpatterns = [
url(r'^celery_test/,views.celery_test),
]
? ? ? ?app/views.py
def celery_test(request):
? ? from tasks import add
? ? add.delay(4,8)
? ? return HttpResponse('Celery testing666')
? ? ? ?<6>開(kāi)啟djanog服務(wù)和celery服務(wù)(雖然耦合了,但是還是需要額外開(kāi)啟)
#python manage.py runserver 0.0.0.0:8000
#另一個(gè)窗口開(kāi)啟celery服務(wù)
#python manage.py celery worker --loglevel=info
? ? ? ?<7>發(fā)送http的GET請(qǐng)求,調(diào)用celery去執(zhí)行異步任務(wù)(大功告成)
curl http://127.0.0.1:8000/index/celery_test/
? ? ? ? celery那端的屏幕輸出如下:
[2016-12-01 16:16:00,940: INFO/MainProcess] Received task: app.tasks.add[06a8d603-a7d3-4732-b8f3-ad010d531200]
[2016-12-01 16:16:00,941: WARNING/Worker-1] 0
[2016-12-01 16:16:01,943: WARNING/Worker-1] 1
[2016-12-01 16:16:02,945: WARNING/Worker-1] 2
[2016-12-01 16:16:03,947: WARNING/Worker-1] 3
[2016-12-01 16:16:04,948: WARNING/Worker-1] 4
[2016-12-01 16:16:05,950: WARNING/Worker-1] 5
[2016-12-01 16:16:06,952: WARNING/Worker-1] 6
[2016-12-01 16:16:07,954: WARNING/Worker-1] 7
[2016-12-01 16:16:08,955: WARNING/Worker-1] 8
[2016-12-01 16:16:09,957: WARNING/Worker-1] 9
[2016-12-01 16:16:10,958: WARNING/Worker-1] 10
[2016-12-01 16:16:11,959: WARNING/Worker-1] 11
[2016-12-01 16:16:12,961: WARNING/Worker-1] 12
[2016-12-01 16:16:13,962: WARNING/Worker-1] 13
[2016-12-01 16:16:14,964: WARNING/Worker-1] 14
[2016-12-01 16:16:15,964: WARNING/Worker-1] 15
[2016-12-01 16:16:16,966: WARNING/Worker-1] 16
[2016-12-01 16:16:17,968: WARNING/Worker-1] 17
[2016-12-01 16:16:18,969: WARNING/Worker-1] 18
[2016-12-01 16:16:19,971: WARNING/Worker-1] 19
[2016-12-01 16:16:20,973: WARNING/Worker-1] 20
[2016-12-01 16:16:21,974: WARNING/Worker-1] 21
[2016-12-01 16:16:22,976: WARNING/Worker-1] 22
[2016-12-01 16:16:23,978: WARNING/Worker-1] 23
[2016-12-01 16:16:24,979: WARNING/Worker-1] 24
[2016-12-01 16:16:25,981: WARNING/Worker-1] 25
[2016-12-01 16:16:26,982: WARNING/Worker-1] 26
[2016-12-01 16:16:27,984: WARNING/Worker-1] 27
[2016-12-01 16:16:28,986: WARNING/Worker-1] 28
[2016-12-01 16:16:29,987: WARNING/Worker-1] 29
[2016-12-01 16:16:30,990: INFO/MainProcess] Task app.tasks.add[06a8d603-a7d3-4732-b8f3-ad010d531200] succeeded in 30.049149203s: 12