晚十二點,牀上javascript
富婆:這是給你的500元,辛苦了前端
小蝌蚪:不辛苦,主人java
富婆:我老公要回來了,你走吧node
小蝌蚪:是,主人git
說完小蝌蚪從三樓別墅跳了下去程序員
。。。github
小蝌蚪是一名程序員,也是一個技師算法
白天敲代碼,晚上捏腳npm
因爲常年敲代碼,因此指法高超後端
時常把客戶按到上天日龍
。。。
富婆年輕、有錢,可是缺愛
由於嫁給了80歲老公
小蝌蚪聽話活好不粘人
深得富婆的歡心
。。。
又一次見面
富婆:好無聊,趕忙討好我
小蝌蚪:主人,我用手指在您腿上敲一段「Dijkstra最短路徑算法」吧
富婆:這招你用過了
小蝌蚪:那我用胸毛在您背上默寫《javascript高級程序設計》
富婆:不想
小蝌蚪:那我用舌頭給您表演一段口技
富婆:舌頭?口技?
小蝌蚪:不要多想,是正規表演
因而小蝌蚪表演了一段舌頭碎大石
富婆大喜,掌聲經久不衰
。。。
表演完畢,富婆面色泛紅
富婆:我喜歡你
小蝌蚪:我也喜歡我本身
富婆:你愛我嗎
小蝌蚪:我不能對客戶動心
富婆:我要如何才能獲得你的心
小蝌蚪:殺了我,把心掏出來,嘻嘻
富婆:嘻嘻
。。。
富婆:對了,下週來參加個人閨蜜派對
小蝌蚪:不帶你老公去嗎
富婆:他80歲了,帶去丟臉
小蝌蚪:那我可要加錢噢
富婆:多少
小蝌蚪:200塊錢兩小時,物美價廉,服務一流
富婆:deal
。。。
晚八點,高級酒店,各類炫富
閨蜜A:我買了個包,才10萬,好便宜哦~
閨蜜B:我包了三個小奶狗,讓他們打了一夜鬥地主
閨蜜C:我老公養了好多小動物,好比寶馬、路虎、捷豹
富婆默默上前:我在北京二環買了套四合院
其餘閨蜜瞬間不說話了
。。。
小蝌蚪意識到
這不是一個普通的飯局
而是一場閨蜜之間的裝逼盛宴
小蝌蚪:你須要我作什麼
富婆:我須要你戰勝她們的男寵
。。。
酒後三巡
到了炫耀男寵的階段
每一個閨蜜都帶來了本身包養的男寵
有肌肉男、高級鴨、男模等等
肌肉男上臺就展現八塊腹肌和沙包同樣大的褲襠
男模直接亮出大長腿和帥到掉渣的臉
高級鴨把舌頭伸出來,在臺上發出「略略略略略...」的聲音
男寵們各有所長
贏得了場下閨蜜們的掌聲
。。。
輪到小蝌蚪上臺
一身屎綠色格子襯衫
呆若木雞
主持人問:辣雞,你要展現什麼特長
小蝌蚪:舌頭碎大石
主持人:除了這個還有嗎
小蝌蚪:胸毛碎大石
主持人暴怒:我懷疑你是來砸場的
小蝌蚪:是的,我就是來砸場的
主持人叫來20個保安
十秒鐘後保安所有倒地抽搐
身上被小蝌蚪的胸毛刻滿了神祕javascript代碼
。。。
主持人:爸爸對不起,剛纔是我冒犯了
主持人:爸爸,麻煩跟觀衆介紹一下本身
小蝌蚪從書包裏掏出一千萬美金(富婆送)
點燃後從98樓外拋出
燃燒的錢漫天飛舞
小蝌蚪介紹道:「我是顏色不同的渣男」
臺下全部女人狂轟亂叫
因過分興奮而七竅流血
男寵們看到本身的主子瘋狂跪舔小蝌蚪
深感本身被綠了
主持人:爸爸您還有什麼溫柔點的特長展現給你們嗎
小蝌蚪:我要唱一首孫豔枝的《綠光》,送給在座的各位男辣雞
「
《綠光》
愛就像一道綠光
灑在你頭上
都怪嫂子太漂亮
。。。
不要怪爸爸無情
是大家太辣雞
啊~綠光~呀~綠光
照亮你我他
」
男寵們:小蝌蚪你太過度了
小蝌蚪:各憑本事作人渣
男寵們:你到底想幹什麼
小蝌蚪:我想在大家墳頭上蹦迪
男寵們心態爆炸,瘋狂抓頭
從98樓跳了下去
小蝌蚪贏得了比賽
成爲了第一名男寵
。。。
酒店陽臺,微風拂過
富婆:今晚謝謝你
小蝌蚪:爲了錢,應該的
富婆:我喜歡你
小蝌蚪:我也喜歡我本身
富婆:你愛我嗎
小蝌蚪:我不能對客戶動心
富婆:我要如何才能獲得你的心
小蝌蚪:殺了我,把心掏出來,嘻嘻
富婆:嘻嘻
。。。
屋內,捏腳
富婆:我要死了
小蝌蚪:怎麼了
富婆:我老公在外面有女人了
富婆:他是個黑幫大佬,要殺我,跟小三在一塊兒
小蝌蚪輕撫富婆的臉:傻瓜,在這以前,我會殺了他
小蝌蚪和富婆擁吻在了一塊兒
兩嘴之間拉出一條亮絲
吻畢,小蝌蚪消失在夜空中
。。。
深夜十二點
黑幫大佬酒店房間門外
塞入了一張小卡片
上面寫着「包小姐」
和一張小蝌蚪穿泳褲的照片
。。。
黑幫大佬有着詭異的癖好
喜歡胸毛濃密的小奶狗
小蝌蚪知足了他對一切小奶狗的幻想
。。。
房間門果真打開了
小蝌蚪用一千根胸毛扎入了大佬體內
大佬:你。。。你是誰
小蝌蚪:我是你爸爸
大佬:爲何要殺我
小蝌蚪:由於我是你爸爸
大佬:能不能不提爸爸兩個字
小蝌蚪:我和你老婆有一腿
黑幫大佬,猝,享年80
。。。
因爲刺殺了黑幫大佬
當晚回去的路上
小蝌蚪被1000名殺手全城追殺
用盡了最後一根胸毛
小蝌蚪衆寡不敵
倒在了血泊裏,奄奄一息
將死之際,富婆從殺手中出現
她微笑的捅了小蝌蚪一刀
把心掏了出來
小蝌蚪驚恐的望着她
富婆:我終於獲得了你的心,嘻嘻
(完)
做者:第一名的小蝌蚪微信公衆號:前端屌絲
github: https://github.com/airuikun/blog
你們都知道,nodejs啓的後端服務,若是有代碼變更,要重啓進程,代碼才能生效。
nodejs的進程在重啓的時候,爸爸們去訪問服務,就會出現短暫的 502 bad gateway
,爸爸們會不高興
若是你的服務器加上了watch機制
當服務器上的代碼頻繁發生變更,或者短期內發生高頻變更,那就會一直 502 bad gateway
所謂「重啓一時爽,一直重啓一直爽」
近段時間在作雲編譯相關需求的時候,就出現了短期內線上服務代碼高頻變更,代碼功能模塊高頻更新,在不能重啓服務的狀況下,讓更新的代碼生效的場景。
這就涉及到一個熱部署的概念,在不重啓服務的狀況下,讓新部署的代碼生效。
接下來我來給爸爸們講解熱部署的原理和實現方案
當咱們經過require('xx/xx.js')
去加載一個功能模塊的時候,node會把require('xx/xx.js')
獲得的結果緩存在require.cache('xx/xx.js')
中
當咱們屢次調用require('xx/xx.js')
,node就再也不從新加載,而是直接從require.cache('xx/xx.js')
讀取緩存
因此當爸爸在服務器上修改xx/xx.js
這個路徑下的文件時,node只會去讀取緩存,不會去加載爸爸的最新代碼
爲了實現這個熱部署機制,在網上處處查資料,處處踩坑才弄好
如下代碼是提煉出來、完整可運行的熱部署基礎原理代碼,你們能夠基於這個代碼去自行拓展:smart-node-reload
nodejs版本:v10.5.0
git clone
下來之後,無需安裝,直接運行
npm start
這時候就開啓了熱部署變更監聽
如何看到效果呢
爸爸請看/hots/hot.js
文件
const hot = 1 module.exports = hot
將第一行代碼改成const hot = 111
const hot = 111 module.exports = hot
這時候就能看到終端裏監聽到代碼變更,而後動態加載你的最新代碼並獲得執行結果,輸出爲:
熱部署文件: hot.js ,執行結果: { 'hot.js': 111 }
熱部署服務監聽到代碼變更,並從新加載了代碼,爸爸們就能夠實時拿到最新代碼的執行結果了,整個過程都在線上環境運行,node進程也沒有重啓
const handlerMap = {};// 緩存 const hotsPath = path.join(__dirname, "hots"); // 加載文件代碼 並 監聽指定文件夾目錄文件內容變更 const loadHandlers = async () => { // 遍歷出指定文件夾下的全部文件 const files = await new Promise((resolve, reject) => { fs.readdir(hotsPath, (err, files) => { if (err) { reject(err); } else { resolve(files); } }); }); // 初始化加載全部文件 把每一個文件結果緩存到handlerMap變量當中 for (let f in files) { handlerMap[files[f]] = await loadHandler(path.join(hotsPath, files[f])); } // 監聽指定文件夾的文件內容變更 await watchHandlers(); };
loadHandlers
是整個熱部署服務的主函數,咱們指定了服務器根目錄下的hots
文件夾是用來監聽變更和熱部署的文件夾
用fs.readdir
掃描hots
文件夾下的全部文件,經過loadHandler
方法去加載和運行每個掃描到的文件,將結果緩存到handlerMap
裏
而後用watchHandlers
方法開啓文件變更監聽
// 監視指定文件夾下的文件變更 const watchHandlers = async () => { // 這裏建議用chokidar的npm包代替文件夾監聽 fs.watch(hotsPath, { recursive: true }, async (eventType, filename) => { // 獲取到每一個文件的絕對路徑 // 包一層require.resolve的緣由,拼接好路徑之後,它會主動去幫你判斷這個路徑下的文件是否存在 const targetFile = require.resolve(path.join(hotsPath, filename)); // 當你適應require加載一個模塊後,模塊的數據就會緩存到require.cache中,下次再加載相同模塊,就會直接走require.cache // 因此咱們熱加載部署,首要作的就是清除require.cache中對應文件的緩存 const cacheModule = require.cache[targetFile]; // 去除掉在require.cache緩存中parent對當前模塊的引用,不然會引發內存泄露,具體解釋能夠看下面的文章 //《記錄一次由一行代碼引起的「血案」》https://cnodejs.org/topic/5aaba2dc19b2e3db18959e63 //《一行 delete require.cache 引起的內存泄漏血案》https://zhuanlan.zhihu.com/p/34702356 if (cacheModule.parent) { cacheModule.parent.children.splice(cacheModule.parent.children.indexOf(cacheModule), 1); } // 清除指定路徑對應模塊的require.cache緩存 require.cache[targetFile] = null; // 從新加載發生變更後的模塊文件,實現熱加載部署效果,並將從新加載後的結果,更新到handlerMap變量當中 const code = await loadHandler(targetFile) handlerMap[filename] = code; console.log("熱部署文件:", filename, ",執行結果:", handlerMap); }); };
watchHandlers
函數是用來監聽指定文件夾下的文件變更、清理緩存更新緩存用的。
用fs.watch
原生函數監聽hots
文件夾下文件變更,當文件發生變更,就算出文件的絕對路徑targetFile
而require.cache[targetFile]
就是require
對targetFile
原文件的緩存,清除緩存用require.cache[targetFile] = null;
坑爹的地方來了,僅僅只是將緩存置爲null,會發生內存泄露,咱們還須要清除緩存父級的引用require.cache[targetFile].parent
,就是下面這段代碼
if (cacheModule.parent) { cacheModule.parent.children.splice(cacheModule.parent.children.indexOf(cacheModule), 1); }
// 加載指定文件的代碼 const loadHandler = filename => { return new Promise((resolve, reject) => { fs.readFile(filename, (err, data) => { if (err) { resolve(null); } else { try { // 使用vm模塊的Script方法來預編譯發生變化後的文件代碼,檢查語法錯誤,提早發現是否存在語法錯誤等報錯 new vm.Script(data); } catch (e) { // 語法錯誤,編譯失敗 reject(e); return; } // 編譯經過後,從新require加載最新的代碼 resolve(require(filename)); } }); }); };
loadHandler函數的做用是加載指定文件,並校驗新文件代碼語法等。
經過fs.readFile讀取文件內容
用node原生vm模塊vm.Script方法去預編譯發生變化後的文件代碼,檢查語法錯誤,提早發現是否存在語法錯誤等報錯
檢驗經過後,經過resolve(require(filename))方法從新將文件require加載,並自動加入到require.cache緩存中
以上就是熱部署的全部內容了,代碼地址是:smart-node-reload
這個代碼是我通過極簡後的代碼,方便你們閱讀和理解,感興趣的小夥伴能夠經過這個代碼去進行深一步拓展
哎。。。。寫完這篇文章,已是凌晨兩點了,寫文章真不容易,麻煩各位爸爸star、follow、點贊和關注,辛苦爸爸了
做者:第一名的小蝌蚪微信公衆號:前端屌絲
github: https://github.com/airuikun/blog