前文提到咱們經過網關把流量轉發到Node 應用,那網關是如何肯定 Node 應用的可用性呢?css
若是Node 應用在發佈的過程當中也把流量轉發過來,就會致使請求失敗,因此咱們的網關會對Node 應用作一個健康檢查,要首先肯定 Node 應用是健康的,也就是能夠對外服務的。具體來講就是網關會每隔30秒調一下 Node 應用的健康檢查的 HTTP 接口,若是接口返回的 code 是200,那就表示 Node 應用是可用的,用戶的請求在下次檢查以前都會轉發過來,若是返回其餘 code,表示應用不可用,請求就不會轉發過來。過30秒再重複這個過程。html
【示意圖】前端
這個方案實現起來很是簡單,只要再Node 添加個能正常請求的 HTTP 接口便可,好比咱們用的接口 `/health/check`它的 controller 內就 `this.ctx.body = 'OK'`就能夠了。若是 Node 應用正常啓動,能夠接受用戶請求,那麼這個接口返回 code 就會是200,若是這個接口不能正常訪問,返回的code不是200,那麼也意味着整個應用是不能訪問的。webpack
那上面這個方案就沒有問題了嗎?確定是有的,好比咱們在發佈時候,首先要讓Node 應用下線,若是剛好 Node 應用剛被健康檢查經過後就下線了,那麼就會致使後面30秒內轉發到 Node 應用的流量訪問失敗,因此咱們有了升級方案-平滑發佈。web
平滑發佈就要跟發佈系統進行配合了,就是咱們在發佈應用的時候發佈系統會自動調用Node 應用的下線接口,發佈完成以後會調用 Node 應用的上線接口,這樣就能夠經過一個全局變量控制應用的狀態,而這個狀態是和應用的真實狀態沒有關係的。調用下線接口後,應用狀態置爲下線,而後等待一段時間才真正讓應用下線,因此若是這時有流量進來應用依然能夠正常服務。json
【示意圖】 安全
邏輯很簡單,可是實現的時候要考慮到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 可能會奇怪, 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`也能夠做爲咱們後續代碼分離後的方案之一。
技術方案的選擇通常要結合團隊已有的技術方案和業務需求,本文介紹的平滑發佈方案在咱們業務前期,確實解決了咱們的發佈問題,讓發佈變得更安全。
可是隨着業務發展咱們須要灰度環境,來更好的確保應用的健康狀態,提早發現應用中的問題。另外咱們還須要知道咱們的應用的運行狀態,因此在下一講內容中,咱們會分享關於灰度發佈和應用監控相關的內容。