使用django+celery+RabbitMQ實現異步執行

文章出自: http://blog.charlee.li/django-celery-rabbitmq-intro/


RabbitMQ大家應該不陌生,著名的消息隊列嘛。可惜我最近才聽說它的大名,瞭解之後不禁驚呼,世界上居然還有這種東西! 
立刻覺得手裏有了錘子,就看什麼都是釘子了,主網站不願意乾的操作統統扔給RabbitMQ去做吧 :D

言歸正傳,先介紹一下這篇文章的應用場景吧。我們知道大型網站的性能非常重要,然而有時不得不做一些相當耗時的操作。 比如SNS網站的“新鮮事兒”系統,我發帖之後,會給所有關注我的人推送一條通知。乍一看沒什麼難的,發帖之後找出關注我的人, 然後生成相應的消息記錄就行了。但問題是,100個人關注我,就要執行100條INSERT查詢,更要命的是,Web服務器是同步的, 這100條查詢執行完成之前,用戶是看不到結果的。

怎麼辦呢,這時就輪到消息隊列上場了。發帖之後只需給隊列發送一條消息, 告訴隊列“我發帖子了”,然後把發帖的結果返回給用戶。 這時另一個叫做worker的進程會取出這條消息並執行那100條INSERT查詢。這樣,推送通知的操作在後臺異步執行, 用戶就能立即看到發帖結果。更精彩的是,可以運行多個worker實現分佈式,多繁重的任務都不在話下了。

好了,來看看今天的主角:

  • django:web框架,其實只能算作配角了;
  • RabbitMQ:消息隊列系統,負責存儲消息;
  • celery:worker進程,同時提供在webapp中創建任務的功能。

django-celery-rabbitmq-intro-1.png

安裝

安裝環境是MacOS,使用其他操作系統的同學請自行調整安裝命令。

django的安裝配置就不說了,配角嘛。

先來安裝RabbitMQ:

$ sudo port install -n rabbitmq

啓動RabbitMQ:

$ 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 並安裝:

$ 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 djcelery
djcelery.setup_loader()

BROKER_HOST = "localhost"
BROKER_PORT = 5672
BROKER_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

@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解放出來吧。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章