RabbitMQ你們應該不陌生,著名的消息隊列嘛。惋惜我最近才據說它的大名,瞭解以後不由驚呼,世界上竟然還有這種東西! 馬上以爲手裏有了錘子,就看什麼都是釘子了,主網站不肯意乾的操做通通扔給RabbitMQ去作吧 :D
言歸正傳,先介紹一下這篇文章的應用場景吧。咱們知道大型網站的性能很是重要,然而有時不得不作一些至關耗時的操做。 好比SNS網站的「新鮮事兒」系統,我發帖以後,會給全部關注個人人推送一條通知。乍一看沒什麼難的,發帖以後找出關注個人人, 而後生成相應的消息記錄就好了。但問題是,100我的關注我,就要執行100條INSERT查詢,更要命的是,Web服務器是同步的, 這100條查詢執行完成以前,用戶是看不到結果的。
怎麼辦呢,這時就輪到消息隊列上場了。發帖以後只需給隊列發送一條消息, 告訴隊列「我發帖子了」,而後把發帖的結果返回給用戶。 這時另外一個叫作worker的進程會取出這條消息並執行那100條INSERT查詢。這樣,推送通知的操做在後臺異步執行, 用戶就能當即看到發帖結果。更精彩的是,能夠運行多個worker實現分佈式,多繁重的任務都不在話下了。
好了,來看看今天的主角:
django:web框架,其實只能算做配角了;
RabbitMQ:消息隊列系統,負責存儲消息;
celery:worker進程,同時提供在webapp中建立任務的功能。python
安裝
安裝環境是MacOS,使用其餘操做系統的同窗請自行調整安裝命令。
django的安裝配置就不說了,配角嘛。
先來安裝RabbitMQ:web
$ sudo port install -n rabbitmq
啓動RabbitMQ:shell
$ sudo rabbitmq-server -detached
而後安裝celery。到這裏下載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這個模塊。 到這裏下載 django-celery 並安裝:
django
$ 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,若有須要請自行下載編譯。
應用程序示例
創建測試應用程序:
json
$ django-admin.py startproject celerytest $ cd celerytest $ django-admin.py startapp hello $ cd hello
而後修改settings.py,在INSTALLED_APPS中加入如下內容:
bash
INSTALLED_APPS = ( ... 'djcelery', # 加入celery 'hello', # 測試應用程序 }
在settings.py末尾添加RabbitMQ的配置:
服務器
import djcelery djcelery.setup_loader() BROKER_HOST = "localhost" BROKER_PORT = 5672 BROKER_USER = "guest" BROKER_PASSWORD = "guest" BROKER_VHOST = "/"
固然,別忘了配置數據庫選項,由於djcelery要用到數據庫的。配置好以後執行:
app
$ python manage.py syncdb
能夠執行 python manage.py 看一下,會發現 djcelery 應用程序給manage.py添加了許多celery*開頭的命令, 這些就是控制worker的命令了。
接下來咱們寫個task。新建 hello/tasks.py,內容以下:
框架
from celery.decorators import task @task def 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解放出來吧。
批註:最後一段不敢苟同,我以爲消息隊列仍是很可靠的,處理關鍵任務也應該是沒問題,作好日誌和任務信息備份,照樣能夠很穩定的運行。