開(kāi)始使用Celery調(diào)度任務(wù)如果引入調(diào)度,周期性執(zhí)行或者不阻塞請(qǐng)求線程,很多Django應(yīng)用可以得到更好的使用。 在Django應(yīng)用里,有很多方式可以用來(lái)調(diào)度任務(wù),但是使用Celery有許多優(yōu)勢(shì)。它有很好的支持,伸縮性好,并且能和Django一起工作。它應(yīng)用廣泛,有很多資源可以幫助學(xué)習(xí)和使用它。一旦學(xué)會(huì)了,對(duì)其他項(xiàng)目也有好處。 Celery 3.0.x 本文檔適用于Celery 3.0.x。之前或之后的版本可能有差別。 Celery簡(jiǎn)介 Celery被用來(lái)稍后執(zhí)行某些代碼,或用調(diào)度器調(diào)度這些代碼 這有什么用呢?下面有一些例子。 第一個(gè)例子,假設(shè)用戶發(fā)來(lái)一個(gè)頁(yè)面請(qǐng)求,然后等待請(qǐng)求完成,瀏覽器加載新頁(yè)面。對(duì)于他們的請(qǐng)求,你需要運(yùn)行一些代碼,而這些代碼的運(yùn)行時(shí)間可能比用戶想要等待網(wǎng)頁(yè)的時(shí)間要長(zhǎng),但是你并不真得需要在響應(yīng)頁(yè)面請(qǐng)求前執(zhí)行那些代碼。這時(shí),你如果使用Celery稍后執(zhí)行這些耗時(shí)的代碼,就立即響應(yīng)頁(yè)面請(qǐng)求。 當(dāng)你需要連接遠(yuǎn)程服務(wù)器來(lái)處理請(qǐng)求時(shí)會(huì)經(jīng)常遇到這種情況。你的應(yīng)用不能控制遠(yuǎn)程服務(wù)器響應(yīng)的時(shí)間,甚至遠(yuǎn)程服務(wù)器可能是關(guān)閉的。 另一種常見(jiàn)情形是需要周期性執(zhí)行某些代碼。比如,你可能每小時(shí)查詢最新的天氣預(yù)報(bào)并且儲(chǔ)存數(shù)據(jù)。你可以寫(xiě)個(gè)任務(wù)來(lái)執(zhí)行這項(xiàng)工作,然后設(shè)置Celery每個(gè)小時(shí)執(zhí)行一次。這個(gè)任務(wù)運(yùn)行并把數(shù)據(jù)存入數(shù)據(jù)庫(kù),然后Web應(yīng)用就可以獲得最新的天氣預(yù)報(bào)。 一個(gè)任務(wù)(task)只是一個(gè)Python函數(shù)。你可以把調(diào)度一個(gè)任務(wù)理解為延時(shí)調(diào)用哪個(gè)函數(shù)。比如,你可以需要 Celery 5分鐘后使用參數(shù)(1, 3, 3)調(diào)用你的函數(shù)task1,或者你可以使函數(shù)batchjob每晚0時(shí)執(zhí)行。 我們將會(huì)設(shè)置Celery來(lái)使你的任務(wù)在和其余應(yīng)用代碼盡可能相似的環(huán)境中執(zhí)行,以使它們使用同樣的數(shù)據(jù)庫(kù)和Django設(shè)置。有一些差異需要記住,稍后將會(huì)講到。 當(dāng)一個(gè)任務(wù)準(zhǔn)備執(zhí)行時(shí),Celery將它放到一個(gè)隊(duì)列上,這個(gè)隊(duì)列上有其它的將要執(zhí)行的任務(wù)。你可以有很多隊(duì)列,但是這里為了簡(jiǎn)單我們假設(shè)只有一個(gè)隊(duì)列。 可以這樣說(shuō),將一個(gè)任務(wù)添加到隊(duì)列上僅僅只是把它加入到一個(gè)待辦列表。為了執(zhí)行任務(wù),一些其它的程序--任務(wù)執(zhí)行單元(worker)--將監(jiān)視隊(duì)列上是否有任務(wù)。當(dāng)它發(fā)現(xiàn)隊(duì)列上有任務(wù)時(shí),它會(huì)取出并執(zhí)行第一個(gè)任務(wù),然后接著監(jiān)視隊(duì)列等待其它任務(wù)。你可以有很多任務(wù)執(zhí)行單元,可能在許多不同的服務(wù)器上,但是我們今天假設(shè)只有一個(gè)任務(wù)執(zhí)行單元。 稍后我們會(huì)講更多關(guān)于隊(duì)列,任務(wù)執(zhí)行單元和其他尚未提及的重要的程序,上面這些現(xiàn)在已經(jīng)夠用了,讓我們做一些工作。 本地安裝Cerely 安裝本地Django使用的Celery很簡(jiǎn)單--只需安裝django-celery:
首先,我們配置Celery在runserver上使用。對(duì)于Celery中間件(broker,稍后介紹),將使用Django database broker implementation?,F(xiàn)在,你只需知道Celery需要一個(gè)中間件,在開(kāi)發(fā)過(guò)程中可以使用Django自帶的(但是在生產(chǎn)環(huán)境中你必須使用一些更健壯的性能更好的) 在Django settings.py文件中: 1.添加這些代碼:
注意:絕對(duì)不要在生產(chǎn)環(huán)境中使用Django中間件。在本教程中我們使用它只是為了節(jié)省時(shí)間。在生產(chǎn)環(huán)境中,你可以使用RabbitMQ或者Redis。 2.在INSTALLED_APPS中添加djcelery和kombu.transport.django:
3.創(chuàng)建Celery數(shù)據(jù)庫(kù),如果使用South作模式遷移:
如前所述,一個(gè)任務(wù)可以僅僅只是一個(gè)Python函數(shù)。但是,Celery必須了解它。當(dāng)Celery和Django一起工作時(shí),這很容易。只需要在你的應(yīng)用中添加一個(gè)tasks.py文件,把你的任務(wù)放在那個(gè)文件中,并且裝飾它們。這里有一個(gè)簡(jiǎn)單的tasks.py:
將一個(gè)函數(shù)標(biāo)記為任務(wù)并不影響它正常工作。你仍可以如此調(diào)用它:z = add(1, 2)并且它會(huì)和以前一樣工作。將它標(biāo)記為任務(wù)可以讓你用其它方式調(diào)用。 調(diào)度任務(wù) 接著上面的簡(jiǎn)單例子。我們想立即運(yùn)行任務(wù),并且不想它阻塞當(dāng)前線程。只需通過(guò)在任務(wù)的名字后面添加 .delay 即可實(shí)現(xiàn):
這很重要,你的任務(wù)總是導(dǎo)入并且引用相同的包名稱(chēng)。比如,依賴(lài)于你的Python路徑如何設(shè)置,可能這樣指向它myproject.myapp.tasks.add或者myapp.tasks.add?;蛘邚膍yapp.views,你可能這樣導(dǎo)入它 .tasks.add。但是Celery無(wú)法知道這些都是同一個(gè)任務(wù)。 djcelery.setup_loader()將會(huì)使用你的應(yīng)用在INSTALLED_APPS里的包名,加上 .tasks.functionname。確保當(dāng)你調(diào)度你的任務(wù),你也使用同樣的名字導(dǎo)入它,否則可能會(huì)出現(xiàn)bugs。 測(cè)試 啟動(dòng)一個(gè)任務(wù)執(zhí)行單元 正如我們提過(guò)的,一個(gè)單獨(dú)的程序,任務(wù)執(zhí)行單元,用來(lái)執(zhí)行你的Celery任務(wù)。下面是我們?nèi)绾螁?dòng)一個(gè)任務(wù)執(zhí)行單元滿足開(kāi)發(fā)需要。 首先,打開(kāi)一個(gè)新終端或者新窗口。在終端里,設(shè)置相同的Django開(kāi)發(fā)環(huán)境--啟動(dòng)你的虛擬環(huán)境或者把它們添加到你的Python路徑里,這兩種方法都可以使你使用runserver運(yùn)行你的項(xiàng)目。 現(xiàn)在,你可以在那個(gè)終端里這樣啟動(dòng)一個(gè)任務(wù)執(zhí)行單元:
運(yùn)行任務(wù) 回到你的第一個(gè)窗口,啟動(dòng)一個(gè)Django終端,運(yùn)行你的任務(wù):
前面我們提到過(guò)使用Celery來(lái)避免對(duì)一個(gè)頁(yè)面請(qǐng)求的響應(yīng)延遲。下面是一個(gè)使用這種技術(shù)的簡(jiǎn)單的Django事例。 問(wèn)題處理 嘗試使Celery任務(wù)工作可能會(huì)很困難,因?yàn)闀?huì)使用很多部分,并且這些部分之間還會(huì)相互聯(lián)系。許多常見(jiàn)的小技巧仍然起作用:
也有一些Celery專(zhuān)用工具。 Eager scheduling 在你的Django設(shè)置里,可以添加:
這就是說(shuō),設(shè)置了CELERY_ALWAYS_EAGER = True后,這兩個(gè)語(yǔ)句變得一樣:
查看隊(duì)列 只要你在開(kāi)發(fā)時(shí)使用Django自帶的中間件,你的隊(duì)列就存儲(chǔ)在Django數(shù)據(jù)庫(kù)里。這樣你就可以很容易的查看它。在你的應(yīng)用里向admin.py添加幾行:
檢查結(jié)果 任何時(shí)候調(diào)度一個(gè)任務(wù),Celery都會(huì)返回一個(gè)AsyncResult對(duì)象。你可以保存那個(gè)任務(wù),然后稍后使用它檢查任務(wù)是否執(zhí)行完成,是否成功,以及結(jié)果。
另一個(gè)常見(jiàn)例子是周期性調(diào)度任務(wù)。Celery使用另一個(gè)程序celerybeat來(lái)實(shí)現(xiàn)。Celerybeat一直運(yùn)行,等一個(gè)調(diào)度任務(wù)到執(zhí)行時(shí)間了,celerybeat就會(huì)把它加入隊(duì)列去執(zhí)行。 顯而易見(jiàn),只有一個(gè)celerybeat程序可以運(yùn)行(不像任務(wù)執(zhí)行單元,只要你需要,你就可以運(yùn)行任意個(gè)) 啟動(dòng)celerybeat和啟動(dòng)一個(gè)任務(wù)執(zhí)行單元相似。打開(kāi)另一個(gè)窗口,設(shè)置Django環(huán)境,然后:
添加這條配置信息:
默認(rèn)計(jì)劃任務(wù) 如果你想你的一些任務(wù)默認(rèn)計(jì)劃任務(wù),不依賴(lài)于某人在安裝完你的應(yīng)用后在數(shù)據(jù)庫(kù)里配置它們,你可以使用Django fixtures把你的計(jì)劃任務(wù)作為應(yīng)用的初始數(shù)據(jù)。
提示和技巧 不要把model對(duì)象傳遞給任務(wù) 因?yàn)槿蝿?wù)不會(huì)立即運(yùn)行,當(dāng)一個(gè)任務(wù)運(yùn)行并使用傳入的model對(duì)象時(shí),數(shù)據(jù)庫(kù)的對(duì)應(yīng)紀(jì)錄可能已經(jīng)改變。如果任務(wù)此時(shí)對(duì)model對(duì)象做了一些修改并且保存起來(lái),數(shù)據(jù)庫(kù)里的這些改變就會(huì)被舊數(shù)據(jù)覆蓋了。 更安全的做法是保存對(duì)象,傳遞紀(jì)錄的鍵名,在任務(wù)里重新獲得對(duì)象。
執(zhí)行一個(gè)任務(wù)的時(shí)候調(diào)度另一個(gè)完全可行。這可以用于保證第二個(gè)任務(wù)在第一個(gè)任務(wù)做完一些必要的工作之前不會(huì)運(yùn)行。 不要在一個(gè)任務(wù)里等待另一個(gè)任務(wù) 如果一個(gè)任務(wù)等待另一個(gè)任務(wù),它的任務(wù)執(zhí)行單元將一直阻塞而不能做其他事情直到等待結(jié)束。這早晚造成死鎖。 如果你的任務(wù)A里想要調(diào)度任務(wù)B,在任務(wù)B結(jié)束之后,做一些其他的工作,最好創(chuàng)建一個(gè)任務(wù)C做這些工作,在任務(wù)B結(jié)束時(shí)調(diào)度任務(wù)C。 下一步 一旦你理解了這些基礎(chǔ)部分,最好閱讀一下Celery用戶手冊(cè)中的部分內(nèi)容。我建議從這些章節(jié)開(kāi)始;其他的要不和Django沒(méi)有關(guān)系,要不太過(guò)高級(jí): 在生產(chǎn)環(huán)境里使用Celery 這里描述的Celery配置是為了開(kāi)發(fā)方便,絕不能應(yīng)用在生產(chǎn)環(huán)境。 為了在生產(chǎn)環(huán)境里使用,最重要的修改是停止使用kombu.transport.djanggo作為中間件,使用RabbitMQ或者其他健壯的可伸縮的等價(jià)物替換。 英文原文:https://www./blog/2014/06/23/scheduling-tasks-celery/ |
|
來(lái)自: River_LaLaLa > 《Python》