什麼是Celery?
- Celery是一個簡單、靈活且可靠的,處理大量消息的分佈式系統
- 專注於實時處理的異步任務隊列
- 同時也支持任務調度
如圖所示,整個Celery架構由4部分組成:user、broker、workers、task result。其中:
user負責生成需要處理的任務,然後交給broker任務隊列,等待被處理,workers可以由多個worker組成,然後從broker中獲取需要出處理的任務進行處理,處理完成後,將處理結果放入task result中。
換一種說法:A(user)在不停的做漢堡包,每做好一個漢堡包就將其放在桌子上(broker),桌子上坐着3個人(workers),每個人都從桌子上拿漢堡包喫,沒喫完一個就在本子上做上記錄(task result)。
大概說明了一下Celery,然後說說在Django中的應用
這裏假設一個場景:有一個博客網站,每當有用戶訪問博客網站的文章時,其文章的被瀏覽次數就進行加1操作。
對於這種情況,最簡單的方法就是在後端實時處理,就是當有用戶訪問的時候,在後端的業務邏輯中直接對文章的被瀏覽次數做加1操作,然後將文章返回給前端呈現在用戶的面前。但是這種方式有一個較大的問題,就是每次訪問都會進行一次加1操作,而對數據庫進行寫入操作的成本是遠高於讀取操作的,因此很有可能會影響頁面的響應速度。
基於這種情況,我們就可以使用Celery來實現異步處理,將文章被瀏覽次數的加1操作使用異步的方式來處理,這樣就會大大的降低頁面的響應時間。
下面就基於以上場景來實現在Django中使用Celery實現異步任務處理。
首先在Django項目的settings.py的同級目錄下創建一個celery_config.py的文件(注意我的項目名稱是my_project,根據項目名稱做相應的更改):
import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'my_project.settings') # 設置django環境
app = Celery('my_project')
app.config_from_object('django.conf:settings', namespace='CELERY') # 使用CELERY_ 作爲前綴,在settings中寫配置
app.autodiscover_tasks() # 發現任務文件每個app下的tasks.py文件
然後在Django項目的settings.py的同級目錄下的__init__.py文件中添加如下內容(我的項目名稱是my_project):
from my_project.celery_config import app as celery_app
__all__ = ['celery_app']
接下來,我們就可以在settings.py文件中配置消息隊列broker和結果存儲task result了。我們這裏使用的是redis:
CELERY_BROKER_URL = 'redis://:123456@localhost:6379/1' # Broker配置,使用Redis作爲消息中間件
CELERY_RESULT_BACKEND = 'redis://:123456@localhost:6379/2' # BACKEND配置,這裏使用redis
這時,我們已經配置好了消息隊列中間件以及結果存儲了,接下來還有異步任務生成以及消費者worker了,這裏我們可以先在項目中啓動worker,然後等待異步任務的產生:
celery -A my_project worker -l info
這時worker已經啓動了,接下來我們需要去添加異步任務,基於以上的場景,需要在訪問一個博客詳情的接口時,對該博客的被瀏覽次數進行統計(加1操作)。博客詳情接口如下:
class ArticleDetailApi(APIView):
"""獲取博客文章詳情"""
def get(self, request, article_id):
is_find = False
detail = None
status = 404
article = Articles.get_one(article_id)
if article:
is_find = True
status = 200
detail_serializer = ArticleDetailSerializers((article, ), many=True)
detail = detail_serializer.data
data = {
'is_find': is_find,
'detail': detail,
}
return Response(data, status=status)
這時還沒有加入異步任務,現在我們需要在該接口所在的app下面創建一個tasks.py文件用來存放異步任務,然後添加異步任務:
from django.db.models import F
from blog.models import Articles
from celery import shared_task
@shared_task
def browse_times_task(article_id):
"""博客瀏覽次數異步處理(瀏覽次數加1操作)"""
Articles.objects.filter(id=article_id).update(browse_times=F('browse_times') + 1)
這時異步任務已經定義好了,接下來需要在接口函數中引入該異步任務:
class ArticleDetailApi(APIView):
"""獲取博客文章詳情"""
def get(self, request, article_id):
is_find = False
detail = None
status = 404
article = Articles.get_one(article_id)
if article:
is_find = True
status = 200
detail_serializer = ArticleDetailSerializers((article, ), many=True)
detail = detail_serializer.data
browse_times_task.delay(article_id) # 添加異步任務
data = {
'is_find': is_find,
'detail': detail,
}
return Response(data, status=status)
現在我們嘗試來訪問該接口
請求了幾次發現被瀏覽次數並沒有發生改變,然後查看後臺才發現worker報錯了:
然後我在網上查了後發現這是worker在win10下運行會發生的錯誤,需要在啓動worker是增加一個參數:
celery -A my_project worker -l info -P eventlet
這樣就可以了,這時我們再嘗試訪問博客詳情接口,發現連續請求該接口後,被瀏覽次數這個參數值是變化的,後臺也沒有報錯,這樣就成功了: