Celery 是一個廣泛應(yīng)用于網(wǎng)絡(luò)應(yīng)用程序的任務(wù)處理系統(tǒng)功舀。
它可以在以下情況下使用:
在請求響應(yīng)周期中做網(wǎng)絡(luò)調(diào)用鲁纠。服務(wù)器應(yīng)當立即響應(yīng)任何網(wǎng)絡(luò)請求批狐。如果在請求響應(yīng)周期內(nèi)需要進行網(wǎng)絡(luò)調(diào)用觅丰,則應(yīng)在周期外完成調(diào)用盖腕。例如當用戶在網(wǎng)站上注冊時勺鸦,需要發(fā)送激活郵件并巍。發(fā)送郵件是一種網(wǎng)絡(luò)調(diào)用,耗時2到3秒换途。用戶應(yīng)該無需等待這2到3秒懊渡。因此,發(fā)送激活郵件應(yīng)當在請求響應(yīng)周期外完成军拟,celery 就能實現(xiàn)這一點剃执。
將一個由幾個獨立部分組成的大任務(wù)分成多個小任務(wù)。假設(shè)你想知道臉書用戶的時間流懈息。臉書提供不同的端點來獲取不同的數(shù)據(jù)肾档。譬如,一個端點用以獲取用戶時間流中的圖片辫继,一個端點獲取用戶時間流中的博文怒见,一個端點得到用戶的點贊信息等。如果你的函數(shù)需要和臉書的5個端點依此通信姑宽,每個網(wǎng)絡(luò)調(diào)用平均耗時2秒遣耍,你將需要10秒完成一次函數(shù)執(zhí)行。但是低千,你可以把這項工作分為5個獨立的任務(wù)(你很快就會發(fā)現(xiàn)這很容易做到)配阵,并讓 celery 來處理這些任務(wù)馏颂。Celery 可以并行地與這5個端點通信,在2秒之內(nèi)就能得到所有端點的響應(yīng)棋傍。
簡單的 celery 例子
假設(shè)我們有一個函數(shù)救拉,并傳給它一個網(wǎng)址列表。該函數(shù)需要獲取這些網(wǎng)址的響應(yīng)瘫拣。
沒有使用 celery
創(chuàng)建文件celery_blog.py
:
import requests
import time
def func(urls):
start = time.time()
for url in urls:
resp = requests.get(url)
print resp.status_code
print "It took", time.time() - start, "seconds"
if __name__ == "__main__":
func(["http://oneapm.com", "http://jd.com", "https://taobao.com", "http://baidu.com", "http://news.oneapm.com"])
運行:
python celery_blog.py
輸出:

使用 celery
調(diào)用 celery 的程序中最重要的組成部分為 celery worker亿絮。
在 web 應(yīng)用程序注冊的例子中,celery worker 用于發(fā)送郵件麸拄。
在臉書的例子中派昧, celery worker 用于獲取不同的網(wǎng)址。
在我們的 celery_blog.py
例子中拢切, celery worker 用于獲取 URL蒂萎。
celery worker 和你的應(yīng)用程序/腳本是不同的進程,彼此獨立運行淮椰。所以你的應(yīng)用程序/腳本和 celery 需要一些方法來相互溝通五慈。
應(yīng)用程序代碼需要把任務(wù)放在 celery worker 可以取出并執(zhí)行的位置。譬如主穗,應(yīng)用程序代碼將任務(wù)放在消息隊列中泻拦,celery worker 從消息隊列領(lǐng)取任務(wù)并執(zhí)行任務(wù)。我們將使用 Redis 作為消息隊列忽媒。
請確認你已安裝 Redis争拐,并可以運行redis-server
。
請確認你已安裝 celery晦雨。
修改文件 celery_blog.py
架曹,如下:
from celery import Celery
app = Celery('celery_blog',bloker='redis://localhost:6379/1')
@app.task
def fetch_url(url):
resp = requests.get(url)
print resp.status_code
def func(urls):
for url in urls:
fetch_url.delay(url)
if __name__ == "__main__":
func(["http://oneapm.com", "http://jd.com", "https://taobao.com", "http://baidu.com", "http://news.oneapm.com"])
代碼解釋:我們需要一個 celery 實例來啟動程序,因此創(chuàng)建了一個名為 app 的 celery 實例金赦。
在3個終端中啟動:
第一個終端音瓷,運行 redis-server
第二個終端,運行 celery worker -A celery_blog -l info -c 5
夹抗,通過輸出可以看到 celery 成功運行。
第三個終端纵竖,運行腳本 python celery_blog.py
可以看到第二個終端輸出如下:

將 celery 代碼和配置保存在不同文件中
上面的例子中漠烧,我們只寫了一個 celery 任務(wù)。但您的項目可能涉及多個模塊靡砌,您可能希望在不同的模塊中有不同的任務(wù)已脓。所以讓我們將 celery 配置移到單獨的文件中。
創(chuàng)建 celery_config.py
from celery import Celery
app = Celery('celery_config', broker='redis://localhost:6379/0', include=['celery_blog'])
修改 celery_blog.py
代碼如下:
import requests
from celery_config import app
@app.task
def fetch_url(url):
resp = requests.get(url)
print resp.status_code
def func(urls):
for url in urls:
fetch_url.delay(url)
if __name__ == "__main__":
func(["http://oneapm.com", "http://jd.com", "https://taobao.com", "http://baidu.com", "http://news.oneapm.com"])
停掉之前的 celery worker
通殃,運行:
celery worker -A celery_config -l info -c 5
打開 ipython 度液,運行如下命令:
In [1]: from celery_blog import func
In [2]: func(["http://oneapm.com", "http://jd.com", "https://taobao.com", "http://baidu.com", "http://news.oneapm.com"])
輸出如下:

在不同文件中添加新的任務(wù)
您可以添加新的模塊厕宗,并在該模塊中定義一個任務(wù)。用以下內(nèi)容創(chuàng)建一個模塊 celery_add.py
:
from celery_config import app
@app.task
def add(a, b):
return a + b
改變 celery_config.py
包含新的模塊 celery_add.py
堕担,如下:
from celery import Celery
app = Celery('celery_config', broker='redis://localhost:6379/0', include=['celery_blog', 'celery_add'])
在 ipython 輸入:
In [1]: from celery_add import add
In [2]: add.delay(4, 5)
輸出如下:

在不同的機器上分開使用 Redis 和 celery
到目前為止已慢,我們的腳本、celery worker 和 Redis 都運行在同一機器中霹购。其實并無這種必要佑惠,這三者可以運行在不同機器上。
celery 任務(wù)涉及到網(wǎng)絡(luò)請求齐疙,因此膜楷,在網(wǎng)絡(luò)優(yōu)化的機器上使用 celery worker 能提高任務(wù)運行速度。Redis 是一種內(nèi)存數(shù)據(jù)庫贞奋,在內(nèi)存優(yōu)化的機器上運行效率更高赌厅。
在這個例子中,我將在本地系統(tǒng)運行腳本和 celery worker轿塔,在分開的服務(wù)器上運行 Redis察蹲。
修改 celery_config.py
為:
app = Celery('celery_config', broker='redis://192.168.118.148:6379/0', include=['celery_blog'])

現(xiàn)在我運行任何任務(wù),腳本都將把他放在 Redis 運行的服務(wù)器(192.168.118.148)上面催训。
celery worker 也與 192.168.118.148 溝通,在這個 Redis 服務(wù)器上得到任務(wù)并執(zhí)行它洽议。
注意:您必須使用正在運行 redis-server 的服務(wù)器地址。我的服務(wù)器已停止Redis,所以你將無法連接到 Redis漫拭。
參考文章:Getting started with Celery and Redis
本文系 OneAPM 工程師整理亚兄。OneAPM 是應(yīng)用性能管理領(lǐng)域的新興領(lǐng)軍企業(yè),能幫助企業(yè)用戶和開發(fā)者輕松實現(xiàn):緩慢的程序代碼和 SQL 語句的實時抓取采驻。想閱讀更多技術(shù)文章审胚,請訪問 OneAPM 官方博客。