大規模 Node.js 應用

lolek 9年前發布 | 24K 次閱讀 Node.js Node.js 開發

在剛剛過去的 15 年天貓雙十一中,Node.js(后文簡稱 node) 大放異彩,不僅幫助前端團隊快速、高效的解決雙十一各個業務上的頁面渲染問題,同時在性能和穩定性上也表現非常出色,大大降低了雙十一硬件成本的同時,在 整個雙十一期間未出現任何一起由 node 引發的線上故障。

覆蓋業務

經過一年時間的改造和推進,到 15 年雙十一的時候,已經有大量的業務都有了 node 的身影,基本上天貓大部分的 web 頁面都是通過 node 渲染出來:

  • 天貓首頁、大部分天貓頻道頁、雙十一會場以及所有天貓的活動頁面都全部基于 node 應用提供服務。
  • 商品詳情、店鋪和搜索頁等主流程鏈路上,以及天貓超市和天貓會員等業務線上的頁面渲染。
  • 提供給內部運營小二的天貓頁面搭建平臺 web 層基于 node 進行開發,雙十一期間在此平臺上搭建了超過 1000+ 個雙十一相關活動頁面。
  • </ul>

    工作職責

    在上述覆蓋了 node 的業務中,node 在其中扮演了多種角色:

    完整的 web 應用

    天貓頁面搭建平臺即是一個由 node 負責整個 web 端包括業務邏輯和模板渲染等工作的應用。基于支付寶的 node web 框架 chair,通過 hsf 調用和淘寶共建的頁面數據存儲的接口,用 node 完成業務邏輯處理、頁面渲染和前端接口。

    輕量級的模板渲染容器

    通過 node 整合前端的天貓組件規范 MUI,開發了一套專注于模板渲染的 node 容器(wormhole),通過這個 node 容器,前端可以專注于展現層的開發,統一前端的本地和線上的代碼運行環境,也讓后端擺脫了繁瑣的套模板工作,專注于提供數據接口。同時這套容器基于天貓的 模塊化規范,橫向打通了各個業務和應用之間的模塊共享。

    基于這個模板容器,我們完成了商品詳情、店鋪、搜索頁以及超市等業務線上的前后端分離工作,大大提升了前端的開發效率,并有效降低了前后端溝通成本。

    頁面渲染服務

    同樣基于天貓前端的組件規范 MUI 和模板渲染的 node 容器,我們完成了一套模塊化搭建頁面的系統,同時開發并運維了一個用來渲染基于模塊搭建的頁面的服務,同時這個服務和阿里的 cache CDN 打通,在保證滿足業務需求的前提下,降低消耗的計算資源。

    基于這個服務,在雙十一中提供了 900+ 活動頁面的渲染,以及天貓首頁和各個頻道頁的渲染工作,天貓的所有營銷引流頁面基本都由這個服務提供頁面。

    進入正題

    上面講了許多我們用 node 做了什么,以及覆蓋了那些業務,現在我們來看看,到底我們是怎樣用 node 解決實際的業務需求的。

    拿這次雙十一的會場頁舉例:

    1. 用戶在不同的終端環境下訪問 https://1111.tmall.com 這個網址,請求會直接來到 CDN 上。
    2. CDN 對用戶的終端環境進行判斷,并在內存中找到對應終端的緩存文件返回,若未命中緩存,則繼續往下執行。
    3. CDN 將請求轉發到 node 渲染服務,根據終端類型選擇不同的頁面響應(pc 頁面,h5 頁面, react-native 頁面)。CDN 響應用戶請求,并緩存頁面。
    4. </ol>

      在上述流程中,我們看到同一個 url 對應到后端其實是完全不同的頁面輸出內容,為了達到這個目的,我們和 CDN 團隊一起做了許多工作:

      1. 開發了一個 tengine-detector 組件,通過請求的user-agent以及約定的一些 cookie 信息,判斷用戶的終端類型。并部署到 CDN 上,讓 CDN 擁有了終端判斷的能力。
      2. 用戶請求到 CDN 上之后,CDN 會根據用戶的終端類型分類,設置一個請求頭,例如:detector: pc表明這個請求的終端設備是 PC 上的瀏覽器。
      3. 渲染服務獲取到這個頭之后,根據 url 和設備類型選擇不同的頁面返回。返回時設置 vary 為detector,保證 CDN 根據不同的設備類型緩存不同頁面。
      4. </ol>

        上面提到會根據終端類型對于同一個 url 返回不同的頁面,而這些頁面其實都是通過一個基于 node 開發的天貓頁面搭建平臺用模塊搭建的。在這個平臺上,超過 95% 的模塊都擁有 pc 和無線兩個版本,本次雙十一所有用到的模塊都有 react native 的版本。運營只需搭建 PC 上的頁面,就會自動生成無線以及 react native 的頁面。基于這套方案,我們通過 70+ 高質量的模塊,讓運營同學完成了超過 900+ 活動頁面的搭建。

        再深入一點,我們如何來完成這些頁面或者是模塊的呢?首先,我們希望讓前端開發做什么?

        • 編寫模板
        • 拿到數據(并處理),和模板進行結合
        • 拿到請求上下文,時間、環境等系統變量來確定不同的展現
        • 管理前端資源和依賴
        • </ul>

          我們在 xtemplate 模板引擎的基礎上進行擴展,讓前端通過編寫 xtemplate 模板,在 context 中注入一些必需的頁面上下文,擴展 xtemplate 的語法,支持引入前端資源。基于這套模板,我們可以在拿到數據后渲染得到完整的頁面,基本滿足了開發頁面在功能上的所有需求。

          但是頁面中其實有非常多重復性的內容,我們完全可以把他們抽象成一個個的模塊,讓頁面通過模塊化的方式來基于模塊搭建,在這個過程中我們需要解決幾個問題。

          1. 模塊版本和靜態資源版本的管理:頁面可能引用幾十個模塊,而這些模塊依賴的靜態資源有重復、有沖突,因此我們會通過一份統一的seed來進行依賴版本的管理,每一個模塊在發布的時候都會打包好自身的依賴關系,而在將所有的模塊組合成頁面的時候,將所有模塊的依賴表重新進行合并和去重,最終保證頁面引用的模塊和靜態資源唯一。同時我們在模板中通過擴展引入了FELoader(天貓的靜態資源加載器),收集頁面的所有靜態資源,combo 后插入到頁頭(css)或者頁尾(js)。
          2. 模塊如何拿到相應的數據:對于模塊而言,他并不需要知道被哪個頁面引用了,所有的頁面在引用模塊的時候需要將模塊所需的數據傳遞進去。而所有的模 塊開發者需要編寫一份模塊需要數據的 JSON Schema 描述,通過這份描述文件,搭建平臺、投放系統以及其他使用這個模塊的人都能夠知道要為這個模塊產生什么格式的數據。
          3. 配套的搭建平臺和數據投放平臺來讓運營自由組合所有的模塊生成頁面,并為頁面上的每一個模塊進行數據投放。
          4. </ol>

            解決完上述問題之后,我們將每一個頁面都變成了以下幾個部分:

            1. 一份頁面的描述文件,聲明了這個頁面依賴的所有模塊,以及渲染這些模塊所需的數據的地址。
            2. 一系列相互獨立的模塊。
            3. 一份包含頁面上所有模塊需要的數據的數據文件。
            4. </ol>

              最終,我們的渲染服務會根據 URL 和請求的終端環境,找到對應的頁面描述文件,請求相應的數據,合并所有的模板渲染成為 HTML 頁面。

              當我們完成了 web 頁面的模塊化搭建之后回頭再看,是不是 react native(RN) 的頁面也能夠搭建呢?我們只需要所有的模塊都有對應的 react native 版本,就可以像搭建 web 的 html 一樣搭建渲染出 RN 需要的 js 了!所以本次雙十一使用的所有模塊都有 RN 版本,并有多個會場采用了 RN 進行搭建,取得了非常不錯的效果,在接下來的雙十二中,我們所有的會場都會支持 RN,而這一切對于搭建會場的運營來說都是完全透明的。

              穩定性保障

              在阿里,所有的雙十一相關應用都需要面臨的一個大問題就是穩定性,為了保證能夠在幾億用戶買買買的時候不掉鏈子,任何一個應用都需要花很大的精力來保障它的穩定性,node 的應用也一樣。

              對于 node 應用自身而言,我們首先要保證它有充足的測試,通過 mocha + istanbul ,盡可能讓測試覆蓋每一個功能點和邊緣情況。

              需要有完善的監控和報警。在阿里內部,我們已經有了內部的監控系統,對于 node 應用而言,只需要按照要求的格式打印的日志,或者通過自己編寫日志采集腳本,就可以輕松的搞定監控和報警。

              • 錯誤日志監控:通過采集腳本采集上來并分類,并設置單機報警和閾值和集群報警的閾值,在異常出現時能夠及時發現。
              • 系統狀態監控:內存、CPU、load 等的監控,并設置報警閾值,當系統狀態異常時能夠及時發現。
              • 應用狀態監控:QPS、響應時間以及所有的遠程調用記錄,時刻了解系統的負載和各個依賴節點的服務狀態。
              • </ul>

                同時,對于 node 應用,我們可以使用阿里云團隊提供的 alinode ,他們可以提供更多 node 的日志和監控,并提供了在線的 profiler 和快照功能,方便排查線上異常和性能優化。

                盡管我們可以對自身的代碼做各種測試、各種監控,但是在一個復雜的系統中,各種上下游依賴非常復雜,網絡情況也很復雜,這個時候為了保證穩定性,我們還有許多的工作要做。

                沒有單點

                假設一個機房的光纜被挖斷了,或者機房所在的城市大規模斷電了,然后整個天貓的大部分頁面都不能訪問了,這明顯不能接受,所以我們需要在多個城市的 多個機房部署我們的服務。如果存放模板文件或者數據文件的服務掛了怎么辦?多個節點,主備讀取,同時對所有的文件都加上磁盤文件容災。對外提供服務的整條 鏈路上的每一個依賴都不能夠出現單點問題。

                弱化依賴

                在排除完單點問題之后,我們再來審視我們的服務,是不是所有的依賴在掛掉后就無法正常服務了?是否我們對于每個依賴異常都有容災的方案,弱化掉整條鏈路上的依賴。

                預案自動化

                對于每一個可能出現問題的環節,我們都需要有針對性的預案,如果這個預案需要人工去執行,就需要思考能否做到自動化。在 node 渲染服務中,可能有各個緩解出問題,鏈路上的所有預案都要能夠自動切換:

                • CDN 回源到多個機房,當某個機房異常時能夠通過健康檢查自動剔除。
                • 當源站 load 過高時,服務自動切換到靜態版本不做渲染。
                • 當模板或者數據的存儲節點掛了,通過健康檢查自動剔除。
                • ...
                • </ul>

                  總結

                  再回過頭來看看在天貓我們使用 node 做的事情,不一定很牛逼,但是確實是在天貓現在的業務場景下,一個相對較優的使用方案,不論是在解決前端開發效率、還是提升服務質量方面,都發揮了很重要的作用。而經過了這次雙十一的考驗,我們也認為它已經是一個很成熟的工具,可以幫助我們更好的完成我們的工作。

                  node 只是工具,在每一個具體的業務場景下都有最合適的使用方法,而隨著業務的發展,node 能做的事情也在變化,我們期望它能在之后能在更多的場景下落地。:)

                  </div> </div> </div> 來自:https://github.com/tmallfe/tmallfe.github.io/issues/28

 本文由用戶 lolek 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!