網易智慧企業 Node.js 實踐(2)| 平滑發佈和前端代碼

健康檢查

前文提到咱們經過網關把流量轉發到Node 應用,那網關是如何肯定 Node 應用的可用性呢?css

若是Node 應用在發佈的過程當中也把流量轉發過來,就會致使請求失敗,因此咱們的網關會對Node 應用作一個健康檢查,要首先肯定 Node 應用是健康的,也就是能夠對外服務的。具體來講就是網關會每隔30秒調一下 Node 應用的健康檢查的 HTTP 接口,若是接口返回的 code 是200,那就表示 Node 應用是可用的,用戶的請求在下次檢查以前都會轉發過來,若是返回其餘 code,表示應用不可用,請求就不會轉發過來。過30秒再重複這個過程。html

圖片3.png

                                                                    【示意圖】前端

這個方案實現起來很是簡單,只要再Node 添加個能正常請求的 HTTP 接口便可,好比咱們用的接口 `/health/check`它的 controller 內就 `this.ctx.body = 'OK'`就能夠了。若是 Node 應用正常啓動,能夠接受用戶請求,那麼這個接口返回 code 就會是200,若是這個接口不能正常訪問,返回的code不是200,那麼也意味着整個應用是不能訪問的。webpack

那上面這個方案就沒有問題了嗎?確定是有的,好比咱們在發佈時候,首先要讓Node 應用下線,若是剛好 Node 應用剛被健康檢查經過後就下線了,那麼就會致使後面30秒內轉發到 Node 應用的流量訪問失敗,因此咱們有了升級方案-平滑發佈。web

平滑發佈

平滑發佈就要跟發佈系統進行配合了,就是咱們在發佈應用的時候發佈系統會自動調用Node 應用的下線接口,發佈完成以後會調用 Node 應用的上線接口,這樣就能夠經過一個全局變量控制應用的狀態,而這個狀態是和應用的真實狀態沒有關係的。調用下線接口後,應用狀態置爲下線,而後等待一段時間才真正讓應用下線,因此若是這時有流量進來應用依然能夠正常服務。json

圖片4.png

                                                                    【示意圖】 安全

邏輯很簡單,可是實現的時候要考慮到Egg.js 的多進程模型,Egg.js  通常根據服務器的 CPU 核數來定啓動相應數量的 Worker 進程,這樣就能夠完美利用多核資源。每一個進程裏都跑的是同一份源代碼,這些進程同時監聽一個端口,因此當發佈系統調用下線接口時,只有其中一個進程會收到請求,若是隻是把收到請求的這個進程的全局變量置爲下線的話,其它的進程在收到健康檢查的時候依然返回的是在線狀態,這樣就不對了,因此要使用進程間通訊,告訴全部進程下線。服務器

基於這些分析咱們實現了Egg.js 插件 `pp-ndp` ,另外因爲 Egg.js 插件中不容許有路由,因此咱們經過中間件的形式實現,主要代碼以下:app

```工具

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`也能夠做爲咱們後續代碼分離後的方案之一。

總結

技術方案的選擇通常要結合團隊已有的技術方案和業務需求,本文介紹的平滑發佈方案在咱們業務前期,確實解決了咱們的發佈問題,讓發佈變得更安全。

可是隨着業務發展咱們須要灰度環境,來更好的確保應用的健康狀態,提早發現應用中的問題。另外咱們還須要知道咱們的應用的運行狀態,因此在下一講內容中,咱們會分享關於灰度發佈和應用監控相關的內容。

相關文章
相關標籤/搜索