對(duì)多進(jìn)程部署的一次感受

因?yàn)橹俺隽艘粋€(gè)線上部署了404鏈接的小事故,組內(nèi)決定在原有的定時(shí)監(jiān)控上加一個(gè)“每10分鐘檢測(cè)一次線上404次數(shù)”的告警。

因?yàn)楸O(jiān)控的數(shù)據(jù)都是通過(guò)clickhouse存儲(chǔ),所以數(shù)據(jù)的采集需要寫(xiě)clickhouse的SQL腳本,這次寫(xiě)還get了獲取clickhouse日期的方法,之前都是通過(guò)js手動(dòng)獲取當(dāng)天日期,然后插到sql中去查詢。其實(shí)clickhouse的SQL語(yǔ)句已經(jīng)內(nèi)置了時(shí)間相關(guān)的函數(shù),具體可以看這里(感謝翻譯大佬的貢獻(xiàn),不過(guò)想微微吐槽一下翻譯把API也給翻譯成了中文,導(dǎo)致看半天發(fā)現(xiàn)怎么沒(méi)發(fā)現(xiàn)對(duì)應(yīng)的api,建議中英文結(jié)合查看)

言歸正傳,說(shuō)回這個(gè)告警腳本,這個(gè)告警的原理是每10分鐘查一次數(shù)據(jù),看看線上404的告警會(huì)不會(huì)超過(guò)閾值,如果超出則告警到企業(yè)微信群。

一開(kāi)始寫(xiě)的時(shí)候就想到了一旦數(shù)據(jù)超出,那么每10分鐘就會(huì)收到一條告警,這樣工作群里就會(huì)充斥告警信息,污染了工作群的聊天記錄。所以還需要開(kāi)發(fā)一個(gè)告警屏蔽功能,打開(kāi)屏蔽功能后即使超出閾值,告警也不會(huì)繼續(xù)發(fā)送。

屏蔽功能簡(jiǎn)單的實(shí)現(xiàn)就是在告警程序上加一個(gè)/close接口,只要調(diào)用接口并傳入對(duì)應(yīng)的定時(shí)腳本名稱,指定的定時(shí)腳本就會(huì)停止告警。再寫(xiě)一個(gè)/open接口允許定時(shí)腳本繼續(xù)運(yùn)行。

/close傳入的腳本名稱就需要保存在一個(gè)地方,這樣每次告警腳本運(yùn)行的時(shí)候都判斷一次,是否要執(zhí)行當(dāng)前的告警腳本。那么,存在哪里合適呢?

說(shuō)到存儲(chǔ),最先想到的就是數(shù)據(jù)庫(kù)了,使用數(shù)據(jù)庫(kù)存取數(shù)據(jù)。但是一個(gè)數(shù)組大小的數(shù)據(jù),用數(shù)據(jù)庫(kù)就有點(diǎn)小題大做了。

第二種方法是腳本生成一個(gè)JSON文件,通過(guò)讀寫(xiě)JSON文件里的配置,控制定時(shí)腳本的運(yùn)行。這是個(gè)好辦法,但是程序手動(dòng)讀寫(xiě)文件總是給我一種“萬(wàn)一讀寫(xiě)出錯(cuò)了怎么辦”的不安全感。于是我使用了第三種方法。

因?yàn)槎〞r(shí)腳本是Egg.js寫(xiě)的,Egg程序可以讀寫(xiě)config這個(gè)屬性,那么我只要把需要屏蔽的腳本名保存為config里的一個(gè)屬性就可以了。于是,我在config里配置了一個(gè)disableSchedule屬性:

// config.default.js
export default = appInfo => {
  const userConfig = {
    // 關(guān)閉的告警
    disableSchedule: [],
   };
}

然后通過(guò)/close/open讀寫(xiě)disableSchedule數(shù)組,豈不是大功告成?而且本地測(cè)試的時(shí)候也是沒(méi)問(wèn)題的。于是寫(xiě)完后,第二天就上線了。

多進(jìn)程部署導(dǎo)致的讀取配置不一致

部署的時(shí)候,使用了egg默認(rèn)的npm run start讓程序成功運(yùn)行了起來(lái),功能上線的第一天晚上,404告警就出現(xiàn)了,但是個(gè)問(wèn)題不大的404頁(yè)面,機(jī)智的我馬上開(kāi)啟了告警屏蔽功能,沒(méi)想到10分鐘后,告警又出現(xiàn)了!再過(guò)10分鐘又一條!很顯然,保存在config里的配置并沒(méi)有成功生效,無(wú)奈只好手動(dòng)先把定時(shí)腳本停止了再看問(wèn)題出在哪。

調(diào)用/close 的時(shí)候,明顯配置是成功添加了,但是讀配置的時(shí)候,讀到的配置卻是空的,可以猜測(cè)到腳本可能是讀了一份“不一樣”的配置。一番檢查后發(fā)現(xiàn),有多個(gè)相同的腳本進(jìn)程在機(jī)器上運(yùn)行,難道的egg默認(rèn)開(kāi)啟了多進(jìn)程部署?查了一下文檔發(fā)現(xiàn)還真是:

啟動(dòng)命令
$ egg-scripts start --port=7001 --daemon --title=egg-server-showcase
如上示例,支持以下參數(shù):
--workers=2 框架 worker 線程數(shù),默認(rèn)會(huì)創(chuàng)建和 CPU 核數(shù)相當(dāng)?shù)?app worker 數(shù),可以充分的利用 CPU 資源。

egg 默認(rèn)創(chuàng)建和 CPU 核數(shù)相當(dāng)?shù)?app worker 數(shù),這樣在調(diào)用接口改寫(xiě)配置的時(shí)候,可能修改的是 worker1 的config,定時(shí)任務(wù)讀config的時(shí)候,讀到的卻是 worker2 的配置,導(dǎo)致修改的配置無(wú)效,除非統(tǒng)一把所有線程上的 config 都修改了。或者,將線程改為只啟動(dòng)一個(gè),修改配置:

// package.json
"scripts": {
   "start": "egg-scripts start --daemon --workers=1 --title=egg-server-failUrl-schedule"
}

增加參數(shù)workers=1,將進(jìn)程設(shè)為1個(gè),這樣定時(shí)任務(wù)就會(huì)只讀取 worker1 的配置,不會(huì)出現(xiàn)設(shè)置無(wú)效的問(wèn)題了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容