關(guān)于 node.js 在公司基礎(chǔ)框架中的應(yīng)用

項目名稱:關(guān)于 node.js 在公司基礎(chǔ)框架中的應(yīng)用

修訂歷史

版本號 作者 時間
1.0 廖石榮(0027011057),林飛鵬(0027011438) 2019-01-11

1 概述

1.1 術(shù)語

Node.js 是一個 Javascript 運行環(huán)境(runtime environment),發(fā)布于 2009 年 5 月,由 Ryan Dahl 開發(fā),實質(zhì)是對 Chrome V8 引擎進行了封裝。Node.js 不是一個 JavaScript 框架,不同于CakePHP、DjangoRails。Node.js 更不是瀏覽器端的庫,不能與 jQuery、ExtJS 相提并論。Node.js 是一個讓 JavaScript 運行在服務(wù)端的開發(fā)平臺,它讓 JavaScript 成為與PHPPython、PerlRuby 等服務(wù)端語言平起平坐的腳本語言。

1.2 需求背景

最近部門某個項目在開發(fā)完成初次上線之后,由于接口復(fù)雜,數(shù)據(jù)庫設(shè)計等原因?qū)е陆涌诜祷爻瑫r,特別是多人同時使用這個系統(tǒng),并發(fā)性存在問題,后來通過后端將接口由串行改為并行方式返回數(shù)據(jù),數(shù)據(jù)庫增加索引等方式雖然解決了問題,但是花費的時間比較多而且復(fù)雜,所以后來在想有沒有一個好的架構(gòu)可以避免這種問題,由于畢業(yè)設(shè)計的時候接觸過 node.js,我知道 node.js 可以解決高并發(fā),它是單線程,當(dāng)訪問量很多時,將訪問者分配到不同的內(nèi)存中,不同的內(nèi)存區(qū)做不同的事,以快速解決這個線程。就像醫(yī)院的分科室看病人。效率快,但消耗內(nèi)存大、異步和事件驅(qū)動。概擴起來就三點:單線程、異步 I/O、事件驅(qū)動。nodejs 離不開 ChormeV8 引擎,也就是 V8 引擎是來解釋 javascript。用 nodejs 來搭建高性能的 Web 服務(wù)器,因此 node.js 是基于服務(wù)器端的 javascript。
由于項目規(guī)模越來越大,前后端分離必然是個趨勢,當(dāng)前的分離模式如下圖:

后端 前端
提供數(shù)據(jù) 接收數(shù)據(jù),返回數(shù)據(jù)
處理業(yè)務(wù)邏輯 處理渲染邏輯
Server-side MVC 架構(gòu) Client-side MV* 架構(gòu)
代碼跑在服務(wù)器上 代碼跑在瀏覽器上

這里分離干凈了,分工也很明確了,看似一切都那么美好,but...我們也很容易發(fā)現(xiàn)問題的所在:

  1. Client-side Model 是 Server-side Model 的加工
  2. Client-side View 跟 Server-side 是 不同層次的東西
  3. Client-side 的 Controller 跟 Sever-side 的 Controller 各搞各的
  4. Client-side 的 Route 但是 Server-side 可能沒有

也就是說服務(wù)端和客戶端各層職責(zé)重疊,大家各搞各的,很難統(tǒng)一具體要做的事情。并且可能會伴隨著一些性能上的問題。最具體的表現(xiàn)就是我們常用的 SPA 應(yīng)用:

  1. 渲染,取值都在客戶端進行,有性能的問題
  2. 需要等待資源到齊才能進行,會有短暫白屏與閃動
  3. 在移動設(shè)備低速網(wǎng)路的體驗奇差無比
  4. 渲染都在客戶端,模版無法重用,SEO 實現(xiàn) 麻煩

緊接著,我們代碼量越來越大,我們需要校驗的表單也會越來越多,有時候,前端提交需要校驗一次表單。
服務(wù)端任需要進行校驗來達到數(shù)據(jù)的可靠性;前端的路由可能在服務(wù)端并不存在....等等這一系列重用性的問題。所以我們之前的重構(gòu)可能需要更深層次的思考。

1.3 目標(biāo)

高可用,高并發(fā),穩(wěn)定性,速度快

2 系統(tǒng)構(gòu)架重構(gòu)

在開始重構(gòu)之前,我們需要對前后端界線做一個劃分,也就是說什么是屬于前端的范疇,什么是屬于后端的范疇,最傳統(tǒng)的前后端劃分可能是這樣的:


8.png
8.png

那么問題來了:我們前后端劃分的接線,是依照工作職責(zé)來劃分的前后端;還是依照硬體環(huán)境劃分的前后端?自從了 nodejs 之后,我們可以從工作職能上重新定義前后端的范疇:


9.png
9.png

可以看到,這里的前端比之前多了個 nodejs,也就是在前后端之間我們構(gòu)建了一個 nodejs 服務(wù)作為中間層!
為什么我們選擇的中間層是 nodejs 呢?因為我們把中間層歸在了前端的范疇,那么對前端小伙伴來說,nodejs 畢竟還是個 js,那么從語法角度來說,上收起來應(yīng)該沒有什么問題。其次開發(fā)轉(zhuǎn)移成本也想對較低,不必來回切換語言的邏輯和語法:
  1. 前端熟悉的語言,學(xué)習(xí)成本低
  2. 都是 JS,可以前后端復(fù)用
  3. 體質(zhì)適合:事件驅(qū)動、非阻塞 I/O
  4. 適合 IO 密集型業(yè)務(wù)
  5. 執(zhí)行速度也不差

好了,提前說了這么多東西,那么這個中間層能給我們帶來什么了?要知道引入 nodejs 的開發(fā)成本也是很大的,首先就是多了一層服務(wù),多的不說,單憑傳輸時間,就多了一層的傳輸時間??!下面我們來研究一下什么應(yīng)用場景下的 nodejs 能給我們帶來利大于弊的東西。

3 中間層應(yīng)用場景以及性能問題

引入 nodejs 之后,我們來重新劃分一下前后端的職能:


10.png
10.png

11.png
11.png

這個就是中間層 nodejs 的主要思路,下面我們來看一下常見的業(yè)務(wù)場景:

3.1 常見業(yè)務(wù)場景

3.1.1 接口數(shù)據(jù)可靠性修復(fù)

有的時候服務(wù)端返回給我們的數(shù)據(jù)可能并不是前端想要的結(jié)構(gòu),所有用到的展現(xiàn)數(shù)據(jù)都是后端通過異步接口(AJAX/JSONP)的方式提供的,前端只管展現(xiàn)。但是后端經(jīng)常提供后端的數(shù)據(jù)邏輯,在前端還需要去處理這些數(shù)據(jù)邏輯。比如我再開發(fā)一個功能的時候,有時候會碰到這樣的問題:


12.png
12.png
13.png
13.png

服務(wù)端返回的某個字段為 null 或者服務(wù)端返回的數(shù)據(jù)結(jié)構(gòu)太深,前端需要不斷寫這樣的代碼去判斷數(shù)據(jù)結(jié)構(gòu)是否真的返回了正確的東西,而不是個 null 或者 undefined:

if (params.items && params.items.type && ...) {
   // todo
}

對于這種情況,我們前端其實不應(yīng)該去重復(fù)校驗數(shù)據(jù)的格式,這也本不應(yīng)該是瀏覽器端 js 需要做的事情。我們可以在中間層做接口轉(zhuǎn)發(fā),在轉(zhuǎn)發(fā)的過程中做數(shù)據(jù)處理。而不用擔(dān)心數(shù)據(jù)返回的問題:

router.get("/buyer/product/detail", (req, res, next) => {
  httpRequest.get("/buyer/product/detail", (data) => {
    // todo 處理數(shù)據(jù)
    res.send(data);
  });
});

3.1.2 頁面性能優(yōu)化 和 SEO

有點時候我們做單頁面應(yīng)用,經(jīng)常會碰到首屏加載性能問題,這個時候如果我們接了中間層 nodejs 的話,那么我們可以把首屏渲染的任務(wù)交給 nodejs 去做,次屏的渲染依然走之前的瀏覽器渲染。(前端換頁,瀏覽器端渲染,直接輸入網(wǎng)址,服務(wù)器渲染)服務(wù)端渲染對頁面進行拼接直出 html 字符串,可以大幅提高首屏渲染的時間,減少用戶的等待時間。這種形式應(yīng)用最廣的比如 Vue 的服務(wù)端渲染,里面也有相關(guān)的介紹。
其次對于單頁面的 SEO 優(yōu)化也是很好地處理方式,由于目前的 ajax 并不被搜索百度等搜索引擎支持,所以如果想要得到爬蟲的支持,那么服務(wù)端渲染也是一種解決方法。

3.2 中間層性能問題

多加了一層通訊,肯定會有一定的性能損耗。但分層帶來的損失,一定能在其他方面的收益彌補回來,而且合理的分層能讓職責(zé)清晰、方便協(xié)作,大大提升開發(fā)效率。也可以通過優(yōu)化通訊方式和協(xié)議,盡可能把損耗降到最低。

一個靜態(tài)化的詳情頁面上有很多(動態(tài))的數(shù)據(jù),用戶資料、評論信息、訂單等等,需要 5、6 個異步請求,node 中間層可以代理這些請求,輕松實現(xiàn) Bigpipe。
在 PC 上你覺得發(fā) 5,6 個異步請求也沒什么,但是在無線端,在客戶手機上建立一個 HTTP 請求開銷很大,有了這個優(yōu)化,性能一下提升好幾倍。

3.3 容易拓展多種語言接口

目前 Node.js 可以支持調(diào)用 python , c++ 等其它語言接口,這樣可以每個模塊選擇最優(yōu)技術(shù)語言解決方案,不用局限于 Java 一種語言了,技術(shù)壁壘也可以打破。

3.4 淘寶常見的需求解決方案

需求:在淘寶,單日四億 PV,頁面數(shù)據(jù)來自各個不同接口,為了不影響體驗,先產(chǎn)生頁面框架后,在發(fā)起多個異步請求取數(shù)據(jù)更新頁面,這些多出來的請求帶來的影響不小,尤其在無線端。
解決方案:在 NodeJS 端使用 Bigpiper 技術(shù),合并請求,降低負擔(dān),分批輸出,不影響體驗。同時可以拆分大接口為獨立小接口,并發(fā)請求。串行 => 并行,大幅縮短請求時間。

3.5 開源的成熟方案

目前 Node.js 業(yè)界比較成熟的方案很多,如:

  • express
  • koa
  • sails
  • loopback
  • thinkjs
  • egg

express
這個是使用最多的框架,也是各個推薦新手入門的框架。
Express 不對 Node.js 已有的特性進行二次抽象,只是在它之上擴展了 Web 應(yīng)用所需的基本功能(個人感覺相當(dāng)于 node 中的 jquery)

  • 封裝了路由
  • 靜態(tài)資源托管
  • 中間件的概念
  • 內(nèi)置了 jade,ejs 模板引擎

個人評價,express 適合小型項目,不適合大型企業(yè)級項目,個人用用還可以,做為快速入門是個很好的選擇,用過之后就可以考慮進入 koa 框架的道路

koa2

koa 是比 express 思想更先進的框架,是 express 原班人馬打造
koa 解決的最大問題,利用 async await 的新語法特性,解決回調(diào)地獄的問題
koa 與 express 最大的不同,個人覺得有 3 點: 1.在于 handler 的處理方法,express 是普通的回調(diào)函數(shù), koa 是利用 ES7 中 Async/Await 的特性,沒有回調(diào),沒有回調(diào),就大大加速了開發(fā)速度這一點而言,已經(jīng)足以讓我們跪舔了
2.koa 是洋蔥中間件模式,執(zhí)行到 next 的時候,會去調(diào)用下一個中間件,下個中間件執(zhí)行完再接著執(zhí)行上個中間件 next 下面的代碼
3.koa 把 request, response 封裝到了同一個上下文對象 content
最為 express 的進化,確實帶來更好的開發(fā)效率,成本只需要學(xué)西一下 async await 的新語法特性??梢哉f作為 express 框架的進階框架是非常好的了

后面的這幾個都是企業(yè)級框架

express =》koa 之后,最大的問題就是開發(fā)項目的時候缺少約束,單人開發(fā)還好,多人的時候,各種目錄結(jié)構(gòu),各種包的選擇,百花齊放的代碼風(fēng)格。都是團隊開發(fā)頭疼的事情。
因為本人之前做過 php 開發(fā),laravel 框架的使用起來很舒服(優(yōu)雅?。?,還有配套的 laravel-admin。所以一直想找一個能像 laravel 這樣的 node 框架提高開發(fā)效率
回到正題,sails , loopback, thinkjs, egg 都是企業(yè)級框架,我們又該如何選擇
Sails 是基于 exrpess 的大而全的框架,MVC 框架,旨在模擬熟悉的 Ruby on Rails 框架的 MVC 模式,但支持現(xiàn)代應(yīng)用程序的需求。捆綁了一個強大的 ORM,即 Waterline。自動生成的 REST API
LoopBack 是建立在 Express 基礎(chǔ)上的企業(yè)級 Node.js 框架,只需編寫少量代碼就能創(chuàng)建動態(tài)端到端的 REST API,一致化的模型關(guān)系和對 API 訪問的權(quán)限控制等
ThinkJS 是國內(nèi) 360 團隊推出的一款面向未來開發(fā)的 Node.js 框架,整合了大量的項目最佳實踐,讓企業(yè)級開發(fā)變得如此簡單、高效。框架底層基于 Koa 2.x 實現(xiàn),兼容 Koa 的所有功能
Egg.js 是《阿里旗下產(chǎn)品》基于 Node.js 和 Koa 的一個 Nodejs 的企業(yè)級應(yīng)用開發(fā)框架,它可以幫助開發(fā)團隊及開發(fā)人員降低開發(fā)和維護成本。Egg.js 則是按照約定進行開發(fā),奉行『約定優(yōu)于配置』,具備提供基于 Egg 定制上層框架的能力、

高度可擴展的插件機制、內(nèi)置多進程管理、基于 Koa 開發(fā),性能優(yōu)異、框架穩(wěn)定,測試覆蓋率高、漸進式開發(fā)、開發(fā)成本和維護成本低等特點。

4 總結(jié)

4.1 對現(xiàn)有團隊的影響

常見的前后端分離的開發(fā)模式中,后端為前端提供了路由結(jié)構(gòu)和頁面的數(shù)據(jù)綁定,前端只需要切頁面和少量的邏輯。
在 node 中間層中,前端不僅僅要切頁面和做頁面邏輯,還要做 url design、頁面數(shù)據(jù)綁定、聯(lián)調(diào)與溝通,還要考慮 SEO 的問題,偽靜態(tài)頁面、title/keyword 設(shè)置、網(wǎng)站地圖,甚至包括錯誤日志等等。雖然前端的工作量增加了不少,但是基于模塊化的開發(fā),讓總體的效率提升了。
對于后端程序員,接口整合的工作交給了前端服務(wù)器進行處理,同時和前端耦合度大大降低,工作量和工作效率都減少了。
另外,由于前后端分離,測試都可以分開來了,專門測試接口的和專門測試 ui 層。分析項目對本系統(tǒng)的影響,例如:

  • 新加的業(yè)務(wù),功能有沒有影響原有的功能
  • 系統(tǒng)的改造對原有業(yè)務(wù)有什么影響
  • 有沒有影響系統(tǒng)對外提供的服務(wù)的約定,這非常重要

4.1 個人期望

我覺得,以后基于 NodeJs 的全棧式開發(fā)的模式將會越來越流行,這也會引領(lǐng)前端步入工程化時代。但是要把 Node 全棧開發(fā)變成一個穩(wěn)定的、方便的開發(fā)工具,還有很多路要走。公司也可以一步一步慢慢往這個方向上靠攏,打造高可用的框架平臺產(chǎn)品。

本文部分圖片段落參考文章: 淘寶前后端分離實踐

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容