1. 程式人生 > >node.js 框架概述

node.js 框架概述

這個框架從2018年1月左右開始開發,主要參考資料是 《Nodejs:擺脫黑工坊發展出一款基礎企業級框架》 和阿里開源的 egg.js ,核心庫是 [email protected]。一開始用一兩週的時間開發了原型,後來在業務的不斷開發中,框架也不斷的進行著更新。

 

現在主要有下面的特性:

 

負載均衡

負載均衡使用的是 pm2,每臺機器都跑著和 cpu 數量相同的程序數。沿用了之前的專案,同時也沿用了之前的專案不用 package.json 中的 script 來啟動和停止專案,而是另外寫了 shell.cfg 配置檔案和 deploy.sh 部署指令碼來啟停專案的做法。之前這種做法是因為最開始的專案用這種方式做了很多配置,現在的專案不需要這種配置,所以後面其實可以放棄這種複雜的做法,直接使用 script 喚起 pm2 來進行程序管理了。

 

日誌
日誌使用的是 log4js。因為 使用了 pm2 ,內網機器又不能安裝 pm2 的外掛,因此不能使用最新版本的 log4js。用的是古老的 1.1.1 版本,配置方式沒有最新版本的結構明晰,但是能正常輸出日誌了。日誌分為請求期間的詳細日誌 app.log 和請求結束時的總結日誌 httpLog.log,此外還有按月歸檔的錯誤級別日誌 errors_2019.01.log。 請求期間的日誌:在每個請求進來的時候設定一個 ctx.sessionId(用的是 uuid/v1),再把 logger.info 改造成 logger.info(sessionId, msg, [msg2]) 這樣的形式,使每一個 log 都帶上了 sessionId,這樣就實現了每個請求中的所有日誌輸出都會帶有一個統一的 id,方便搜尋日誌,排查問題了。另外,通過 console.log = ctx.logger.info 這樣的賦值,使程式碼裡面的所有 console 都能直接使用 log4js 的格式輸出日誌了。這樣就既能方便的使用 log,又不用到處引入 log4js 了。
請求結束的日誌:得益於 [email protected] 優秀的中介軟體執行模型,可以在最開始的中介軟體的 await next() 之後設定一個專門的日誌 httpLog 來單獨記錄本次 http 請求的結果情況。這個日誌有固定的格式,一開始是通過空格分割開的各個欄位組成的字串,類似 sessionId remoteAddr method originalUrl 這樣。後來發現因為 remoteAddr 和 userAgent 中間也有空格,非常影響對日誌進行資料分析,就改成了陣列的形式,如下 [sessionId, remoteAddr, httpVersion, method, originalUrl, referer, status, responseTime, response.length, user-agent],再用 JSON.stringify 格式化一下,就可以得到可讀性和可處理性良好的日誌了。甚至如果冒險使用 eval 這樣的黑科技的話,能直接當做陣列使用。

 

動態匯入 controller 和 service
使用 loader.js 自動化的匯入所有的 controller 和 service 模組,統一掛載到 app.controller 和 requestHandle(ctx, svs, next) 的 svs 上。自動匯入的檔案必須寫在 controller 或者 service 資料夾中,有固定的匯出格式,這樣統一了工程的檔案結構。 controller 和 service 資料夾支援兩層目錄結構,可以把同一個業務的相關檔案放在同一個子資料夾中,方便檢視。 controller 模組的匯出既支援直接匯出函式物件,也支援匯出類,推薦匯出類的方式,因為這樣可以更方便的使用框架提前為類掛載的各種有用功能,比如使用類的話可以在 service 函式中直接用 this.app 獲取到 app 例項,而不用從 controller 中再傳遞過來。

 

統一 router 配置入口
所有的 router 都在同一個 router.js 檔案中,包括通用介面和業務介面。這樣的好處是不用再到處翻檔案去找一個介面的 router 到底定義在哪裡,也不會發生幾個人做了同樣功能通用介面的情況。

應用生命週期


給應用添加了 beforeStart 和 afterStart 的生命週期鉤子,可以在應用的啟動前後執行指定的任務。在實踐中,beforeStart 任務列表中添加了獲取 mongodb 賬號密碼的任務,連線 mongodb 的任務,afterStart 中添加了定時任務和獲取平安開放平臺 access_token 的任務。 新增生命週期不是非常必要,完全可以在專案啟動檔案中新增各個任務,但是這樣一來既會導致啟動檔案中摻入太多業務程式碼,又難以清晰的觀察到專案啟動前後做的各種任務,因此添加了應用生命週期概念,在這個範疇中處理專案啟動前後的任務。

自動設定對應環境中的 config


config 的檔案一共包括三類: 1是 config.default.js,定義了所有環境的通用配置,2是 config.dev.js、config.stg.js、config.prd.js 這一系列檔案,定義了各個環境中的獨特配置,3是 config.js 檔案,功能是根據應用所在環境,自動 merge 對應環境的配置和 default 的配置。 這樣的檔案安排既可以快速的新增通用配置,又可以為不同的環境新增獨特的配置,同時還提供了清晰的配置檔案結構。唯一的確定可能就是檔案有點多了,不過所有配置都只有一個唯一出口 config.js ,因此用起來還是挺方便的。

資料壓縮


使用 koa-compress,對於返回資料大於 2kb 的啟用壓縮。在生產實踐中,通過優化介面返回資料和壓縮,把一個兩百多 kb 的資料優化到了十幾 kb,最終介面返回速度由幾秒鐘優化到了平均幾毫秒。

安全 header 設定


使用 koa-helmet 來為 response 新增安全設定,提供一定的安全防護。

跨域設定


使用 koa-cors 提供跨域支援。在開發和測試環境啟用全部介面跨域支援,方便除錯和測試。生產環境不開啟全部跨域,根據 config 的配置,對特定路由開啟跨域。

定時同步資料


我們的應用分佈在不同的主機上,相互之間除了使用了同一個資料庫之外毫無關聯,我不想把同一份資料使用所有的 程序都同步一遍,於是 多個獨立程序架構下的單程序資料庫同步方案》這篇文章所言,我設計了一個藉助資料庫來實現只會有一個 程序執行同步任務的方案。這個方案完全自治並且支援無縫擴充套件,完美解決了困擾我很長時間的單程序同步問題。

快速熱更新系統設定


有時一些系統設定需要更新,但是又不到重新部署系統的地步,這裡就可以通過預留的介面來更新資料庫中的配置,之後每個程序中的短時間定時任務就會很快的獲取到資料庫中的配置,系統配置就更新了。

功能開關設定


一些特定的任務可能有時需要關掉,這時就可以通過上面的快速熱更新系統設定來快速的更新系統配置,根據業務流程中的開關設定,就可以控制任務的啟停了。這個設定需要侵入到業務程式碼中,在每個需要設定的功能模組中手動新增開關。

快取功能


自研了一個 輕量、完備的快取模組 ,為資料不常變動的介面資料提供快取功能。使用快取能極大的提高系統的併發能力。

資料庫自動掛載


把資料庫和各個表的例項掛載到了 app 上,不用再每個檔案都再引入表例項。

應用錯誤捕獲和請求錯誤攔截、記錄


使用了 process.on('uncaughtError', err) 和 process.on('unhandledRejection', err) 來分別處理未捕獲的錯誤和未處理的 rejected promise。在請求生命週期的最後使用中介軟體攔截系統錯誤,在開發和測試環境返回完整錯誤記錄,生產環境返回錯誤提示。使用分級日誌記錄錯誤資訊。  

未來要實現的功能:

介面安全訪問


通過對接平安開放平臺,瞭解到了一個加強介面安全訪問的方案。前端請求引數中統一新增時間戳,然後獲取所有介面入參的 md5 資料,把 md5 資料也傳送到後臺,這樣後臺就能通過對 md5 的校驗來檢查資料是否有被篡改。這個方案能夠加強介面資料的安全性,但是獲取介面入參的 md5 資料需要消耗 cpu 資源。 另外一個方案是在一個業務流程中,首先要獲取一個 requestId,後續流程中所有的請求都要在入參中加上這個 id。通過這個方案能夠快速的識別出沒有 requestId 的請求為無效請求,但是對於精心偽造的類似請求就需要查詢資料庫了,這裡就需要再加入一個 redis 作為一個公共的第三方快取,會增加資料讀取和網路請求時間。