西北有高樓,上與浮云齊泊业。 --- 西北望高樓
FLASK
Python
小知識點
Web開發(fā)
本文主要通過Flask
項目說明如何實現(xiàn)動態(tài)加載運行中項目的配置。
- 動態(tài)配置加載有什么作用呢啊易?
正常來說吁伺,如果我們項目運行起來之后,已經(jīng)完成了配置的加載租谈。此時篮奄,我們?nèi)绻胍兏恍┡渲美Τ睿瑢崿F(xiàn)如降級開關切換、更改路徑等操作窟却,通常情況下昼丑,我們可以通過平滑重啟服務來重新加載配置,但是在某些時候夸赫,我們不想重啟服務或者因為某些原因不能重啟菩帝,那么我們就需要一些動態(tài)覆蓋加載配置的方式。
- 舉個例子:
我們創(chuàng)建一個Flask應用茬腿,并加載項目配置呼奢。
# config.py
LOG_DIR = "/opt/logs/test_api"
LOG_LEVEL = INFO
# main.py
app = Flask(__name__)
app.config.from_object("config file name")
上面的代碼展示了,我們通過一個配置文件加載App
配置(比如我們操作日志記錄)切平。當我們啟動服務握础,記錄的日志會存儲到 /opt/logs/test_api
中。
當我們程序運行了一段時間之后悴品,突然收到要求禀综,需要我們把日志存放到指定目錄 /opt/logs/logcenter
下,方便日志中心統(tǒng)一采集苔严。
這個時候我們就可以嘗試通過動態(tài)加載配置的方式去替換原來的日志路徑定枷。
Flask
是支持動態(tài)加載配置的,我們在項目運行中届氢,直接通過再執(zhí)行 app.config.from_object
就可以重新加載配置依鸥,重新加載屬于新增與覆蓋式加載。
- Master-Worker啟用多進程服務的動態(tài)加載
通過 Master-Worker
啟用的服務悼沈,我們在動態(tài)加載配置的時候贱迟,需要做到每一個 Worker
進程都要更新,這樣才能保證絮供,我們在使用的時候衣吠,配置是正確的。
需要變更的時候壤靶,我們通過接口
缚俏、MQ
等方式,將配置更新到Redis
或config文件
或其他地方贮乳。
- 通過接口將配置更新到
Redis
# api.py
@check_api_bp.route('/conf/update', methods=['POST'])
def update_conf():
"""更新配置"""
log_dir = get_json('logDir', '配置', valid=[Required()], parm_type=str)
redis_store.set("LOG_DIR", log_dir)
return success(msg='更新成功')
- 方法一:使用時動態(tài)獲取配置
我們可以實現(xiàn)一個裝飾器忧换,通過在每一個調(diào)用的函數(shù)上增加該裝飾器。
# decorators.py
def _check_log_dir():
log_dir = redis_store.get("LOG_DIR")
if log_dir:
current_app.config["LOG_DIR"] = log_dir
current_app.log_dir = log_dir
def check_log_dir(func):
@wraps(func)
def wrapper_func(*args, **kwargs):
_check_switch()
return func(*args, **kwargs)
return wrapper_func
# api.py
@check_api_bp.route('/net/check', methods=['GET'])
@check_log_dir
def net_check():
"""服務健康檢查"""
error_logger(current_app).info("服務健康檢查")
return success(msg='請求成功')
上面?zhèn)未a說明:在調(diào)用 /net/check
接口的時候向拆,需要用到日記記錄亚茬,那么,我們就在該函數(shù)上浓恳,增加 check_log_dir
的裝飾器刹缝。該裝飾器會讀取 Redis
里的 LOG_DIR
配置碗暗,如果讀取到就覆蓋加載到 app.config
上。在使用的時候梢夯,就能使用最新的配置了言疗。
- 方法二:定時任務動態(tài)更新配置
定時任務動態(tài)更新和使用時更新差不多,只不過有一點要明確颂砸,在多進程服務中噪奄,需要在每一個進程下都執(zhí)行定時任務,這樣才能保證使用的 App
能正確的更新配置人乓。
# crontab.py
# 此處使用的定時任務庫 FLASK-APSCHEDULER
@scheduler_store.task("cron", id="crontab_host_update_config", minute="*/5")
def crontab_host_update_config():
current_app = scheduler_store.app
with current_app.app_context():
log_msg = "執(zhí)行定時任務(minute=*/5):{}".format(__name__)
log_dir = redis_store.get("LOG_DIR")
if log_dir:
current_app.config["LOG_DIR"] = log_dir
current_app.log_dir = log_dir
注意: 通過定時任務的方式去動態(tài)加載配置勤篮,有一定的時延。
上面就是動態(tài)加載配置的個人一點心得撒蟀,可以一起探討下。