使用 django middleware 和 celery 隔離業務系統和積分系統的嘗試

行行出bug發表於2018-12-14

遊戲類或是論壇類web應用,到後面很多都會有積分系統:像發成功一篇文章,回覆一條部落格得多少積分這種。積分系統和很多的業務場景都有關係,但是和具體的使用者請求沒有關係。應該將積分系統和具體的業務程式進行隔離,以降低耦合度。筆者要開發的積分系統不需要具有實時性,所以打算用非同步的方式來實現積分計算程式。實現方案:

  • 使用celery實現積分計算和驗證程式
  • 在django middleware 中準備好引數,並非同步呼叫積分系統

django middleware 簡介

Middleware is a framework of hooks into Django’s request/response processing. It’s a light, low-level “plugin” system for globally altering Django’s input or output.以上摘自django官網個人覺得 django middleware 是一種面向方面程式設計(AOP)思想的實現: 對所有的views程式提供統一的無感的處理。實際場景中可用middleware來統一response輸出格式,統一異常處理,還有統一在輸入中加入登入驗證資訊等。新版的自定義middleware方式很簡單:

  1. 新建python檔案
class TestMiddleware:    def __init__(self, get_response):        self.get_response = get_response        # 這後面寫的程式碼對中介軟體進行初始化,只執行一次    def __call__(self, request):        # 這裡寫的程式碼在呼叫view的方法之前執行,可以對request進行處理        response = self.get_response(request)        # 這裡寫的程式碼在呼叫view方法之後執行,可以對response的返回值進行統一格式處理等                return response複製程式碼
  1. 寫好middleware邏輯後,將這個類放到django的settings檔案中
# 注意: django呼叫middle時按照這裡的排列順序來 裝飾 的,越前面的裝飾的越外層,即:# 排在越前面的,在呼叫views方法前越早被執行,呼叫views方法後越晚被執行# 排在越後面的,在呼叫views方法前越晚被執行,呼叫views方法後越早被執行MIDDLEWARE = [    'django.middleware.security.SecurityMiddleware',    'django.contrib.sessions.middleware.SessionMiddleware',    'django.middleware.common.CommonMiddleware',    'django.middleware.csrf.CsrfViewMiddleware',    'django.contrib.auth.middleware.AuthenticationMiddleware',    'django.contrib.messages.middleware.MessageMiddleware',    'django.middleware.clickjacking.XFrameOptionsMiddleware',    'your middleware path',]複製程式碼

寫好這些後,這個middleware就完成了django middleware


django 整合 celery

Celery – Distributed Task QueueCelery is a simple, flexible, and reliable distributed system to process vast amounts of messages, while providing operations with the tools required to maintain such a system.It’s a task queue with focus on real-time processing, while also supporting task scheduling.以上資訊摘自celery官網celery是用python語言開發的分散式任務佇列,可用於任務的同步處理(接收任務處理返回結果),也可用於非同步處理(不接收返回結果)。在django中處理非同步任務(發郵件;訊息推送等)時,celery是個很好的選擇。執行celery需要中介軟體,這裡使用rabbitMQ,下面簡單介紹如何整合。

安裝rabbitMQ

  1. 執行安裝 brew install rabbitmq
  2. 設定環境變數 PATH=$PATH:/usr/local/sbin
  3. 啟動 sudo rabbitmq-server -detached
  4. 設定使用者

$ sudo rabbitmqctl add_user myuser mypassword$ sudo rabbitmqctl add_vhost myvhost$ sudo rabbitmqctl set_user_tags myuser mytag$ sudo rabbitmqctl set_permissions -p myvhost myuser ".*" ".*" ".*"設定好後在celery中直接配置 'amqp://myuser:mypassword@localhost:5672/myvhost'就可以用了

  1. 啟停啟動: sudo rabbitmq-server以後臺執行方式啟動: $ sudo rabbitmq-server -detached停止: $ sudo rabbitmqctl stop Never use 
    kill 
    (kill(1)) to stop the RabbitMQ server(忌直接殺程式!!!)詳細操作見rabbit官網

安裝celery

celery 是用python開發的,可以直接用pip安裝$ pip install -U Celery更多詳細安裝選項見celery官網

django整合celery

django整合celery是將celery的配置嵌入到django的settings檔案中,更重要的,是celery執行任務的時候,能直接使用django專案的執行環境。

  • 配置settings檔案:上面的rabbitMQ安裝設定好後,在settings中直接引入CELERY_BROKER_URL = 'amqp://myuser:mypassword@localhost:5672/myvhost'
  • 編寫celery執行入口檔案celery.py檔案放在settings的同級目錄下
from __future__ import absolute_import, unicode_literalsimport osfrom celery import Celery# set the default Django settings module for the 'celery' program.os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'test_app.settings')app = Celery('msl')# Using a string here means the worker doesn't have to serialize# the configuration object to child processes.# - namespace='CELERY' means all celery-related configuration keys#   should have a `CELERY_` prefix.app.config_from_object('django.conf:settings', namespace='CELERY')# Load task modules from all registered Django app configs.app.autodiscover_tasks()@app.task(bind=True)def debug_task(self):    print('Request: {0!r
}'
.format(self.request))複製程式碼
  • 編寫自定義任務在自定義的app的一級目錄下面新建python檔案,名稱為 tasks.py用於構建自定義的任務。
# Create your tasks herefrom __future__ import absolute_import, unicode_literalsfrom celery import shared_task@shared_taskdef add(x, y):    return x + y@shared_taskdef mul(x, y):    return x * y@shared_taskdef xsum(numbers):    return sum(numbers)複製程式碼
  • 啟停celery要後臺執行方式啟動的,可以使用supervisord等管理程式進行啟停管理。非後臺方式啟動為:celery -A proj worker -l info ;proj 為celery所在目錄的目錄名,不做修改的話就是專案名字。 這樣啟動的使用ctrl + c停止就可以。django整合celery官方示例

積分系統實現嘗試

思路

  • django middleware中,在呼叫業務處理獲得response後,可以通過訪問request和response中的資料確定:
  1. 本次呼叫的具體是哪個業務(request的METHOD和path一起來確定)
  1. 對應的使用者的資訊(需要事先在中介軟體中將使用者資訊加入request)
  2. 呼叫是否成功(response的狀態或是定義的返回碼等)
  3. 返回的資料(從response中獲取)
  • 制定任務規則,在celery task中將對介面的呼叫抽象成一次任務,符合要求的即為完成某一件任務,並對完成的任務做記錄。

簽到介面呼叫:呼叫完成後,在task中判斷,如果此次簽到呼叫成功,則記錄該時間點上完成某個使用者完成一次簽到任務

  • 制定針對任務的積分獎勵規則,在規定時間內完成多少次任務就可以獲得積分。並用剛完成的任務對應獎勵規則,記錄該項規則的任務完成進度,任務完成的記錄積分獲取記錄。

將獎勵規則制定為: 完成一次簽到任務可以獲得1點積分,連續完成十次簽到任務可以額外獲得2點積分。則此次簽到任務完成後,會對應去判斷這兩條規則。第一條肯定直接滿足,記錄下該條規則下籤到獲取積分進度為完成,並記錄積分獲取記錄。第二條如果不滿足,記錄下該規則下調到積分獲取進度為未完成,未完成的就沒有積分獲取記錄了。

庫表設計(精簡版)

from django.db import modelsclass TaskConfig(models.Model):    """任務配置表    task_type 任務型別    task_desc 任務描述    method 方法(GET, POST, ...)    interface_name 介面名    id_group_name 業務主鍵分組名字(正則式中的分組名稱)    status 狀態 (編輯中, 使用中, 廢棄)    created_at 建立時間    deleted_at 刪除時間    updated_at 修改時間    """    class UserTaskRecord(models.Model):    """使用者任務記錄表    user 使用者    task 任務型別(外來鍵)    business_id 業務主鍵(建立部落格的就是哪條部落格的ID)    created_at 建立時間    updated_at 修改時間    delete_at 刪除時間    """        class ScoreRule(SoftDeleteModel):    """積分規表    task_type 任務型別    score_type 積分規則型別    score_desc 積分規則描述    statistical_period 統計週期(每天,每週,每月,每年,無週期,...)    required_number_of_times 所需次數    number_limit_all 獲取人數上限    start_time 規則生效時間    end_time 規則截止時間    status 規則狀態(編輯中和已生效)    can_repeat 任務內容是否可重複(預設為可重複)    created_at 建立時間    deleted_at 刪除時間    updated_at 修改時間    """class UserScoreInfo(SoftDeleteModel):    """使用者積分資訊表    user 使用者    achievement_type 積分規則型別(外來鍵)    required_number 所需任務量    achievement_amount_now 當任務就量    achieved 達成標誌    created_at 建立時間    deadline 計算截止時間    updated_at 修改時間    deleted_at 刪除時間    """    class ScoreRecord(SoftDeleteModel):    """積分記錄    profile 賬戶    user 使用者    serial_number 流水號    trade_amount 積分    amount_before_trade 計算前賬戶積分量    amount_after_trade 計算後賬戶積分量    business_type 業務型別(積分規則描述)    score_info 積分資訊(外來鍵)    location 地點    country 國家    lang_type 語言    created_at 建立時間    updated_at 修改時間    deleted_at 刪除時間    """複製程式碼

到這裡一個簡陋的積分系統就基本完成了,不過這樣制定的規則會有很多限制,比如分享部落格可以獲得積分這種,如果針對某個具體的部落格分享後所獲得積分量不同,這就需要更進一步的設計。

來源:https://juejin.im/post/5c13074a6fb9a049a81f3309#comment

相關文章