遊戲類或是論壇類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方式很簡單:
- 新建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複製程式碼
- 寫好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
- 執行安裝 brew install rabbitmq
- 設定環境變數
PATH=$PATH:/usr/local/sbin
- 啟動 sudo rabbitmq-server -detached
- 設定使用者
$ 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'
就可以用了
- 啟停啟動:
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中的資料確定:
- 本次呼叫的具體是哪個業務(request的METHOD和path一起來確定)
- 對應的使用者的資訊(需要事先在中介軟體中將使用者資訊加入request)
- 呼叫是否成功(response的狀態或是定義的返回碼等)
- 返回的資料(從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 刪除時間 """複製程式碼
到這裡一個簡陋的積分系統就基本完成了,不過這樣制定的規則會有很多限制,比如分享部落格可以獲得積分這種,如果針對某個具體的部落格分享後所獲得積分量不同,這就需要更進一步的設計。