RabbitMQ大家應該不陌生,著名的訊息佇列嘛。可惜我最近才聽說它的大名,瞭解之後不禁驚呼,世界上居然還有這種東西! 立刻覺得手裡有了鎚子,就看什麼都是釘子了,主網站不願意乾的操作統統扔給RabbitMQ去做吧
言歸正傳,先介紹一下這篇文章的應用情境吧。我們知道大型網站的效能非常重要,然而有時不得不做一些相當耗時的操作。 比如SNS網站的“新鮮事兒”系統,我發帖之後,會給所有關注我的人推送一條通知。乍一看沒什麼難的,發帖之後找出關注我的人, 然後產生相應的訊息記錄就行了。但問題是,100個人關注我,就要執行100條INSERT查詢,更要命的是,Web伺服器是同步的, 這100條查詢執行完成之前,使用者是看不到結果的。
怎麼辦呢,這時就輪到訊息佇列上場了。發帖之後只需給隊列發送一條訊息, 告訴隊列“我發文章了”,然後把發帖的結果返回給使用者。 這時另一個叫做worker的進程會取出這條訊息並執行那100條INSERT查詢。這樣,推播通知的操作在後台非同步執行, 使用者就能立即看到發帖結果。更精彩的是,可以運行多個worker實現分布式,多繁重的任務都不在話下了。
好了,來看看今天的主角:
- django:web架構,其實只能算作配角了;
- RabbitMQ:訊息佇列系統,負責儲存訊息;
- celery:worker進程,同時提供在webapp中建立任務的功能。
安裝
安裝環境是MacOS,使用其他動作系統的同學請自行調整安裝命令。
django的安裝配置就不說了,配角嘛。
先來安裝RabbitMQ:
$ sudo port install -n rabbitmq
啟動RabbitMQ:
$ sudo rabbitmq-server -detached
然後安裝celery。到 http://pypi.python.org/pypi/celery#downloads 下載celery並安裝:
$ tar xzvf celery-2.2.7.tar.gz$ cd celery-2.2.7$ python setup.py build$ sudo python setup.py install
這個過程會安裝數個依賴包,包括 pyparsing、kombu、amqplib、anyjson等,如果自動安裝有困難,可以自行下載編譯。
由於要在django中使用,我們還得安裝django-celery這個模組。 到 http://pypi.python.org/pypi/django-celery#downloads 下載 django-celery 並安裝:
$ tar xzvf django-celery-2.2.4.tar.gz$ cd django-celery-2.2.4$ python setup.py build$ sudo python setup.py install
這個過程會安裝依賴包 django-picklefield,如有需要請自行下載編譯。
應用程式樣本
建立測試應用程式:
$ django-admin.py startproject celerytest$ cd celerytest$ django-admin.py startapp hello$ cd hello
然後修改settings.py,在INSTALLED_APPS中加入以下內容:
INSTALLED_APPS = ( ... 'djcelery', # 加入celery 'hello', # 測試應用程式}
在settings.py末尾添加RabbitMQ的配置:
import djcelerydjcelery.setup_loader() BROKER_HOST = "localhost"BROKER_PORT = 5672BROKER_USER = "guest"BROKER_PASSWORD = "guest"BROKER_VHOST = "/"
當然,別忘了設定資料庫選項,因為djcelery要用到資料庫的。配置好之後執行:
$ python manage.py syncdb
可以執行 python manage.py 看一下,會發現 djcelery 應用程式給 manage.py 添加了許多celery*開頭的命令, 這些就是控制worker的命令了。
接下來我們寫個task。建立 hello/tasks.py,內容如下:
from celery.decorators import task@taskdef add(x, y): return x + y
修飾符 @task 將add函數變成了非同步任務。在webapp中調用add並不會立即執行該函數,而是將函數名、 參數等打包成訊息發送到訊息佇列中,再由worker執行實際的代碼(return x + y)。
當然,別忘了必不可少的worker:
$ python manage.py celeryd -l info
在另一個控制台測試一下:
$ python manage.py shell>>> from hello.tasks import add>>> r = add.delay(3,5) # 執行這一行就能在worker的日誌中看到健全狀態>>> r.wait()8
可以看到,add函數是在worker上啟動並執行,實現了非同步效果。當然,隊列的特性決定了任務並不是即時執行的,可能有延遲, 有時甚至還會丟失,因此,隊列不適合執行關鍵任務。而那些執行結果無關痛癢、對即時性要求不高的任務, 就可以大膽地交給RabbitMQ去處理,將WebApp解放出來吧。
轉載自:使用django+celery+RabbitMQ實現非同步執行,作者charlee