Django使用Celery實現異步任務

什麼是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

這樣就可以了,這時我們再嘗試訪問博客詳情接口,發現連續請求該接口後,被瀏覽次數這個參數值是變化的,後臺也沒有報錯,這樣就成功了:

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