作為前端,我們知道 node 在構建方面是成功的,我們也聽說過全棧,那么 node 是否能應用在企業級的后端?一起來看一下騰訊視頻的 NodeJs 改造。
Tip: 故事大概是 2018 年,主角楊浩,來源于:
(資料圖)
騰訊視頻是一個內容型的網頁。
在 2014 年以前使用的是 C++ 動態生成頁面。有兩個問題:
前端不太會維護 C++ 的那套東西C++ 定時生成網頁。有多少個視頻,它就會生成多少個網頁,然后推送到對應的服務器中。如果更改了某個視頻的信息,得等到下次生成網頁才會更新于是打算使用 NodeJs來對其進行改造。
由于騰訊視頻是內容型的網頁,當時有 30% 的流量來自搜索引擎,所以需要更好的 SEO,于是選用 SSR(服務器渲染)。
Tip: Vue_SSR中也提到服務端渲染的優勢:更快的首屏加載、更好的 SEO
NodeJs 扮演的角色如下:
請求經過 cdn,經過 nginx 通過負載均衡訪問 NodeJs 服務,NodeJs 從各個后臺服務拉取數據,渲染好了在返回給前端。
Tip:相當于以前用 c++ 生成頁面,現在由 NodeJs 生成頁面。
打通 RPC 調用rpc(作用類似 http 協議) 就是遠端資源調用,因為 node 需要從各個后臺服務拉取數據。這里涉及4個方面的事情:
負載均衡。node 和后臺服務之間有一層負載均衡,用的是一種類DNS負載均衡,所以得和負載均衡服務交互,拿到每次需要訪問服務器的ipMongo/mysql/redis(redis - 基于鍵值對的內存數據庫) 存儲的打通。比較簡單,就是對應 npm 包的使用后臺私有協議。例如二進制的協議某場景下比http協議好一些監控系統/日志系統Tip: DNS除了能解析域名之外還具有負載均衡的功能
高并發下進程管理node 是單線程,使用 cluster 模塊創建多個 Nodejs 進程,實現高并發和高可用性。但 cluster 還有點缺陷,做了以下幾點優化:
心跳- master 定時給 cluster 發信息,如果有回復說明它還活著,否則就是僵死,就 kill 它內存檢測- 監控 cluster 內存,如果內存過高,可能就是內存泄漏,也殺死它重啟- cluster kill 后,有的應用可能不能用,就需要將其重啟Tip:在 Node.js 中,cluster 模塊提供了一種簡單的方式來創建多個 Node.js 進程,以實現高并發和高可用性。通過集群模塊,開發者可以使用現有的單線程程序代碼,并將其自動拆分到多個子進程中執行,從而充分利用 CPU 和內存資源,提高應用的效率和穩定性。
第二只怪 - 維護 NodeJs終于把 Node 打通了,現在可以用 node 寫點東西了。
要用 node 寫一個穩定的服務,也不是那么簡單。node 很容易掛掉,比如一點語法問題。
Node 人員不足懂前端的人很多,但懂 node 的就相對要少。寫后端需要懂后端那套東西,要會服務器調優,還要懂運維。
為了解決 Node 人員不足,決定使用框架來平滑 node 曲線。
之前要用 node 寫項目難度大,是因為需要經歷這4步:業務邏輯 -> 會寫 NodeJs -> 熟悉 rpc 調用 -> 熟悉運維(性能調優)
現在用框架,只需要寫業務邏輯就能開干。
這里框架主要使用配置化,屏蔽底層復雜的實現,對外暴露友好的配置。就像 webpack,讓前端構建生態非常繁榮。
要做配置化,就得分析 ssr 本質:從各個后臺領取數據,簡單處理后進行渲染。
ssr抽象表示:請求參數 -> 后端數據 + 模板 -> 頁面文本
ssr 公式:內容=f(數據源,模板)
只要將數據源和模板配置化,就可以通過一個函數解決 ssr 的問題。
研究了如下幾種模板:
art-template 國內有名的開源模板引擎es6 template string + vm.runInNewContext(編譯和運行代碼,作用類似 new Function("console.log("1")"))vue ssr、react ssrart-template 中的 forEach 可以使用預編譯語法來實現,由于交互較少,所以無需使用 vue和react。而且 es6 模板速度測試比 vue-server-render 快很多。
所以最終選取第二種方案:es6 template。
數據源的配置用如下一個 json 表示:
module.exports = { video: { url: "http://...." }, vidviewcount: { dependencies: ["video"], url: "protobuf://union.video.qq.com/...." }, rank: { url: "redis://admin:admin@135246:65535/get?key=haha" }}這個 json 表示 ssr 過程中數據獲取邏輯,其中 vidviewcount 通過 dependencies字段指明依賴 video。
這里用 http、protobuf、redis三種協議(方式)獲取數據。一個協議對應一個請求器,不在框架中的協議可以注冊即可。就像這樣:
factory.registerRequestor("http", requestor);function requestor(){ ...}為了增加配置的靈活性,這里增加了幾個 hook:
{ ... fixBefore: function(param){ // 檢測參數合法性 return param }, fixAfter: function(data){ // 檢測返回數據合法性 if(!data.vid){ throw Error("xxx") } return data }, onError: function(e){ return err; }}寫配置就是寫 SSR 邏輯只要學會寫配置就能搞定 ssr 邏輯。
公式:內容=f(數據源,模板)(參數)
ssr 外部用 koa(nodejs 的web框架) 封裝一下就是一個服務:
let app = koa()let ssr = pigfarm(data, template)app.use(async ctx => { ctx.body = await ssr(ctx.query)})第三只怪 - 搶后端飯碗的問題后臺有后臺擅長的地方(邏輯、計算密集),前端有前端擅長的地方(前端網頁優化)。
尋找一個合作共贏的方式。這里做了如下幾個有特色的前端服務:
每次業務邏輯的改動需要經歷長時間的發布和重啟
前面已經將數據源和模板做到了配置化,現在修改邏輯,只需要更改數據庫中的數據源和模板即可,做到熱更新。
v.qq.com 首頁包含27個模塊
富含個性化內容,無法緩存頁面龐大,速度慢全網頁超過40個rpc個性化接口調用慢利用 transfer-encoding:chunked 快速返回首屏數據,后面再加載2、3、4...屏的數據
Tip:BigPipe 是一個前端性能優化技術,采用分塊渲染的方式。transfer-encoding:chunked是一種 HTTP協議中定義的傳輸編碼方式之一。運行服務器在不知道響應體大小的情況下,將響應分成若干個固定大小的快進行傳輸。
前端容災是指在前端應用中,為了保障可靠性和穩定性而采用的一系列技術和策略,以確保即使在系統出現部分異常或錯誤的情況下,仍然可以正常提供服務。比如網絡問題、服務器故障等
這里可以做整頁備份。
js 中用高階函數非常容易實現緩存。請看示例:
function memoize(func) { // 用于緩存 const cache = {}; return function(...args) { const key = JSON.stringify(args); // 如果緩存中有值,直接返回 if (cache[key]) { return cache[key]; } const result = func.apply(this, args); cache[key] = result; return result; };}




