网易智慧企业 Node.js 实践(2)| 平滑发布和前端代码
健康檢查
?
前文提到我們通過網關把流量轉發到 Node 應用,那網關是如何確定 Node 應用的可用性呢?
?
如果 Node 應用在發布的過程中也把流量轉發過來,就會導致請求失敗,所以我們的網關會對 Node 應用做一個健康檢查,要首先確定 Node 應用是健康的,也就是可以對外服務的。具體來說就是網關會每隔30秒調一下 Node 應用的健康檢查的 HTTP 接口,如果接口返回的 code 是200,那就表示 Node 應用是可用的,用戶的請求在下次檢查之前都會轉發過來,如果返回其他 code,表示應用不可用,請求就不會轉發過來。過30秒再重復這個過程。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 【示意圖】
?
這個方案實現起來非常簡單,只要再 Node 添加個能正常請求的 HTTP 接口即可,比如我們用的接口 `/health/check`它的 controller 內就 `this.ctx.body = 'OK'`就可以了。如果 Node 應用正常啟動,可以接受用戶請求,那么這個接口返回 code 就會是200,如果這個接口不能正常訪問,返回的code不是200,那么也意味著整個應用是不能訪問的。
?
那上面這個方案就沒有問題了嗎?肯定是有的,比如我們在發布時候,首先要讓 Node 應用下線,如果恰好 Node 應用剛被健康檢查通過后就下線了,那么就會導致后面30秒內轉發到 Node 應用的流量訪問失敗,所以我們有了升級方案-平滑發布。
?
平滑發布
?
平滑發布就要跟發布系統進行配合了,就是我們在發布應用的時候發布系統會自動調用 Node 應用的下線接口,發布完成之后會調用 Node 應用的上線接口,這樣就可以通過一個全局變量控制應用的狀態,而這個狀態是和應用的真實狀態沒有關系的。調用下線接口后,應用狀態置為下線,然后等待一段時間才真正讓應用下線,所以如果這時有流量進來應用依然可以正常服務。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?【示意圖】
?
邏輯很簡單,但是實現的時候要考慮到 Egg.js 的多進程模型,Egg.js? 一般根據服務器的 CPU 核數來定啟動相應數量的 Worker 進程,這樣就可以完美利用多核資源。每個進程里都跑的是同一份源代碼,這些進程同時監聽一個端口,所以當發布系統調用下線接口時,只有其中一個進程會收到請求,如果只是把收到請求的這個進程的全局變量置為下線的話,其它的進程在收到健康檢查的時候依然返回的是在線狀態,這樣就不對了,所以要使用進程間通信,告訴所有進程下線。
?
基于這些分析我們實現了 Egg.js 插件 `pp-ndp` ,另外由于 Egg.js 插件中不允許有路由,所以我們通過中間件的形式實現,主要代碼如下:
?
```
const { request } = ctx;
const { path, hostname } = request;
if (path === online) {
??? app.messenger.sendToApp(ONLINE, '');
??? ctx.body = 'NDP: Nodejs Is Online';
} else if (path === offline) {
??? app.messenger.sendToApp(OFFLINE, '');
??? ctx.body = 'NDP: Nodejs Is Offline';
} else if (path === check) {
? ctx.body = 'NDP: Nodejs Start Success';
} else if (path === status) {
? if (app[ISONLINE]) {
??? ctx.body = 'NDP: Nodejs Is Online';
? } else {
??? ctx.status = 500;
? }
} else {
? await next();
}
```
?
當然這個方案的前提是有多臺 Node 服務機器,并按分組進行發布。如果只有一臺機器就沒必要這么麻煩了,反正發布一定會導致停服。
?
插件 `pp-ndp` 為了滿足不同業務需求,online、offline、check、status 這四個 URL 是支持自定義配置的。
?
這個方案不僅解決了我們平滑發布的問題,讓發布不再那么恐怖,而且還可以利用這個方案讓應用上線后能夠更好的服務,比如:可以在應用獲取配置之后再把應用置為上線狀態,或者可以在應用成功注冊或連接某服務之后再把應用置為上線狀態。讓應用保證最健康的狀態對外服務。
?
代碼上 CDN 和 代碼發現
?
看到 CDN 可能會奇怪, Node 應用為什么要 CDN,其實是因為我們為了方便使用同構渲染,而把前端代碼和 Node 代碼放在了一個應用里面,雖然這樣解決了服務端渲染代碼訪問問題,但是客戶端代碼還是走 CDN 比較合理。關于 webpack 使用 CDN 網上有很多文章可以參考,我主要介紹下如何發現前端代碼的,包括代碼上 CDN 和模版中插入前端代碼 URL。
?
主要是使用 `webpack-manifest-plugin` 這個 webpack 插件,它會生成一個文件,比如我們用的 `manifest.json`,里面包括前端代碼資源名稱和對應路徑,類似:
?
```
{
? "vendor.js": "/static/f5e0281b/js/vendor.chunk.js",
? "vendor.js.map": "/static/f5e0281b/js/vendor.chunk.js.map",
? "Page.css": "/static/f2065164/css/Page.chunk.css",
? "Page.js": "/static/f2065164/js/Page.chunk.js",
? "Page.js.map": "/static/f2065164/js/Page.chunk.js.map",
}
```
?
只需要把這個文件內列的文件上傳到 CDN 即可,不需自己手動去打包目錄一個一個找。在上傳 CDN 的時候給每個文件保持同樣路徑。使用我們實現的工具 `pp-cdn` 在發布過程中的代碼編譯完成之后進行上傳。在 Node 模版中引用代碼時,使用我們開發的 Egg.js 插件 `pp-just`,使用方式:
?
```html
<script src='{{ctx.just.use("Page.js")}}'></script>
```
?
插件內部也是讀取 `manifest.json` 文件,輸出加上 CDN 域名之后的 URL,比如上面的代碼就轉變為:
?
```html
<script src='https://qiyukf.nosdn.127.net/huke/static/f2065164/js/Page.chunk.js'></script>
```
?
其實之所以這么做,是為了利用前端代碼多版本帶來的好處,我們是使用文件 hash 作為文件路徑的一部分作為多版本控制的,這樣每次發布后編譯后會把新生成的文件路徑寫入 `manifest.json`,然后通過上面講的方式就可以獲取到最新版本的代碼。
?
當然目前 Node 和 前端代碼在一起是不合理的,可能會導致不必要的發布,后續應該會完全分離,但是使用`manifest.json`也可以作為我們后續代碼分離后的方案之一。
?
總結
?
技術方案的選擇一般要結合團隊已有的技術方案和業務需求,本文介紹的平滑發布方案在我們業務前期,確實解決了我們的發布問題,讓發布變得更安全。
?
但是隨著業務發展我們需要灰度環境,來更好的確保應用的健康狀態,提前發現應用中的問題。另外我們還需要知道我們的應用的運行狀態,所以在下一講內容中,我們會分享關于灰度發布和應用監控相關的內容。
總結
以上是生活随笔為你收集整理的网易智慧企业 Node.js 实践(2)| 平滑发布和前端代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网易智慧企业Node.js实践(1) |
- 下一篇: 网易智慧企业 Node.js 实践(3)