大家好~我是
米洛
!
我正在從0到1打造一個開源的介面測試平臺, 也在編寫一套與之對應的完整教程
,希望大家多多支援。
歡迎關注我的公眾號測試開發坑貨
,獲取最新文章教程!
回顧
上一節我們編寫了線上執行Redis命令的功能,頁面也勉強能用了。對於前置條件這塊來說,就好像沙魯吞了17號,已經算半個完全體
了。
我們趁熱打鐵,解決一下因為部署多機器引發的Apscheduler重複執行的問題。
APScheduler帶來的問題
APScheduler其實本質上還是一個定時任務元件
,它並沒有celery那麼強大複雜的系統。針對多臺機器,或者uvicorn(gunicorn)的多個worker,它是會重複執行
的。
這裡感謝一下小右君,他告訴我前面有大坑。
像我們平時那麼啟動:
if __name__ == "__main__":
uvicorn.run(app='main:pity', host='0.0.0.0', port=7777)
這樣其實只開了1個worker
,你想呀,1個工人執行定時任務,當然不存在競爭的問題,但多個工人一起執行,你就存在資訊不對稱
的問題。
工人A到點了,要幹活了,他不知道B也準備幹同樣的活兒,所以任務重複執行的問題,就出現了。
解決問題的方向
我只啟動1個worker
有點傻,而且效能不好使,多臺機器部署依然有問題。
推薦指數: 0顆星
- 初始化sceduler的時候,利用socket佔據一個固定的埠比如2333
埠號只能被1個worker佔領,其他worker拿不到,也就起不來了。但還是不能支援多節點部署,實際上只有1個worker使用,浪費了1個埠
。
推薦指數: ⭐⭐⭐
- 分散式鎖
不夠輕量,需要引入第三方元件如: Redis/Zookeeper/Etcd
。但能很好解決多worker和多節點的問題。
推薦指數: ⭐⭐⭐
辦法很多,但是好用的真不多
。由於我們本身就需要引入Redis,還是秉著不濫用中介軟體的原則,所以我們打算用Redis的分散式鎖。
而關於redis分散式鎖,有很多介紹。我們用牛人們封裝好的RedLock來幫我們解決同時執行問題。
Redlock
比起自己setnx+用lua指令碼保障分散式鎖執行,官方後面給出了redlock的解決方案。更多這些細節可以自行搜尋Redlock
。
我們這邊採用第三方的庫: Redlock來簡化我們的開發。
基本上用with獲取lock,傳入redis節點資訊即可,接著我們就可以編寫相關程式碼了。
我們還是用裝飾器的方法,在想要加鎖的方法引入此裝飾器。key是自己定義的執行key,用於確定鎖的唯一性。
分散式鎖的原理就是多臺裝置同時去試圖建立key,先建立成功的就執行對應的操作。所以對於所有節點來說,key必須都統一起來,並且不能和其他分散式鎖的key衝突。
import functools
import os
from redlock import RedLock, RedLockError
from config import Config
def lock(key):
def decorator(func):
@functools.wraps(func)
async def wrapper(*args, **kwargs):
try:
# 試圖獲取分散式鎖,如果沒有獲取到則會丟擲RedLockError,所以我們這裡捕獲它
with RedLock(f"distributed_lock:{func.__name__}:{key}:{str(args)}",
connection_details=Config.RedisCluster,
):
return await func(*args, **kwargs)
except RedLockError:
print(f"程式: {os.getpid()}獲取任務失敗, 不用擔心,還有其他哥們給你執行了")
return wrapper
return decorator
關於唯一key的確認,我這邊首先加上了distributed_lock
的字首,是因為方便區分其他key,接著通過函式名稱+唯一key確認分散式key,但由於有的方法是帶引數
的,所以我選擇再加一個args,來支援那些同方法不同引數的任務。
運用到pity之中
我們只需要在run_test_plan方法加上lock這個裝飾器即可,總體來說還是非常方便
的。
如果需要測試的話,大家可以用以下命令啟動pity:
uvicorn main:pity --host=0.0.0.0 --port=7777 --workers=4
可以看到,日誌都輸出了4份,因為有4個worker。用這個模式啟動PITY
的話,可以看到對應的效果。
關於Redlock,這節就介紹到這裡了。下一節我們要在前置條件中支援Redis語句,敬請期待吧。