Categories
程式開發

深入淺出動態化SSR服務(三):架構篇


在前面的《開發工具篇》及《SSR服務篇》中,我們已經能夠開發出一個開發靈活、動態化、高性能、高穩定性的單機SSR服務了。但其中過於細節,還仍然不具備能夠承擔 工業級 之名。在現實中我們的系統設計一般是 自上而下高屋建瓴 式地從更整體的架構層面來分析,如此層層遞進,從宏觀認知到微觀認知、步步強化,進而得到一個優秀的系統。

在本章中我們將介紹動態化SSR服務的整體架構,希望能帶領大家感受一下系統設計的有趣之處。當然,於我而言,系統設計只有適合與否之分,並無優劣之分,一個穩定可靠的、能夠靈活拓展、滿足需求的系統設計就是一個好的設計。此篇的目的主要是希望大家能從中舉一反三,改變視角,深刻思考前端工程化的五個字的內涵從而得到提升。

對於一個好的前端架構師而言,既需要更廣泛的其他技術棧的認知(後端/運維/測試等),也需要能從代碼出發,能夠編寫穩健可靠與高性能的代碼。這也是我們強調良好的計算機學科基礎的原因。

架構總覽

此套動態SSR服務架構我們具有三個比較核心的訴求:

  1. 高性能
  2. 高穩定性
  3. 秒級的部署和回滾

同時我們也很清楚地知道,對於前端而言不管是HTMLCSS還是JavaScript或是各種字體與圖片,他們都是 資源,因此整個的設計思路我們都是圍繞 如何高效獲取資源 來進行的。我們先簡單的看一下架構的設計圖,如下:

深入淺出動態化SSR服務(三):架構篇 1

拋開部署部分,其一個請求的路徑如下:

  1. 客戶端請求index.html頁面
  2. CDN回源至負載均衡服務(LBS)
  3. LBS根據IP HASH的負載均衡算法將請求轉發至某單台機中
  4. 單台機中的Nginx檢查是否命中Cache,若命中則直接返回,否則請求SSR服務
  5. SSR服務從數據庫或後端接口中獲取得到對應的頁面數據
  6. SSR服務根據頁面數據得到組件信息並對組件代碼進行本地或遠端的下載
  7. SSR服務成功渲染,開始返回對應數據直至客戶端
  8. 客戶端解析對應的HTML並從CDN中加載其他的靜態資源
  9. CDN回源Combo服務,並返回對應的靜態資源數據
  10. 客戶端渲染頁面並執行相關JavaScript代碼,執行結束

接下來我們會介紹其中比較關鍵的幾點。

抵抗單頁面大流量

要抵抗單頁面的大流量,首先我們自然而然會想到會使用緩存,與此同時我們也需要保證頁面的及時響應,因此針對這個問題我們一般會使用CDN服務。由於CDN遵循 就近原則,因此客戶端請求對應的頁面及其數據是會被自動分配到延遲最低的CDN節點上,如果我們正確的設置對應的HTTP相關緩存,是會得到很好的低延遲加載效果。

那麼當臨近CDN節點緩存失效怎麼辦呢?這個時候CDN根據對應配置也能很好的幫助我們嘗試回源到目標服務器上,從而完成整個數據的加載。並且由於CDN到目標服務器上的網絡一般採用更優化的網絡鏈路,因此相對於客戶端直接請求目標服務器來說,會有很大的低延遲優勢(客戶端到CDN的節點是最鄰近的,CDN到目標服務器是更優化的網絡鏈路)。

但需要注意的是,對於資源的回源HTTP緩存頭設置我們一般需要讓其遵循源站方便業務端控制。同時也需要將其設置為一個比較合理的數值,否則非常容易造成緩存長時間不失效的問題。

防止SSR服務被穿透

現在我們有了CDN幫助我們抵抗單頁面的大流量後就可以高枕無憂了麼?顯然不是的,因為我們有提到,我們的CDN遵循的仍然是回源策略,由於CDN的多節點分佈式特性,在緩存失效後仍然可能會有大量的回源服務器壓力。在Facebook Live的實踐中,其穿透的請求數數據為 1.8%

However, this is still not enough for Facebook’s reach. In fact, according to an article released by Facebook3, this architecture still leaks about 1.8% of requests to the Streaming Server. At their scale, with millions of requests, 1.8% is a huge amount to leak and puts a lot of stress on the Streaming Server.

單看這個數據並不大,但當其放在一個大並發量級下來說是十分可怕的。因此僅僅只有CDN這層緩存是完全不夠足以保護我們的SSR服務不被流量穿透並壓垮的。

為了防止此類Dog Pile問題,我們的架構中使用了NginxProxy Cache Lock來進行保護。當啟用這個配置時,按照Proxy Cache Key緩存元素的標識符,一次只允許一個請求轉發傳遞給SSR渲染服務。同一緩存元素的其他請求將等待緩存中出現響應,或者釋放該元素的高速緩存鎖,直到Proxy Cache Loke Timeout指令設置的時間為止。

如此這般,在我們實際生產上到達真正到達SSR渲染服務的請求量非常低,從而有效保護了SSR服務被穿透造成的不穩定情況。

合併加載的資源請求

在《開發工具篇》我們較為詳細的介紹瞭如何進行sis合併優化,從對ElementUI的結果上來看,儘管我們將對應的對應的輸出模塊數量從230個降低到了51個,但實際上這也遠遠不夠,仍然會有非常多零散小文件。

當然,我們在《開發工具篇》中提到,我們可以在編譯期針對性的對這些小文件特徵進行對應的合併優化,但在我們實際的系統裡並有採取這些措施,而是選擇了讓Combo服務幫助我們完成了對應的操作。如圖所示:

深入淺出動態化SSR服務(三):架構篇 2

我們可以看到實際上通過sis-ssr輸出的加載路徑都是以/??的方式進行打頭,而在到達Combo服務之後其會幫助我們進行對應的合併操作。這樣,不僅減少了編譯時期的複雜,也達到了很好的減少瀏覽器靜態資源請求數量的要求。

但需要注意的是,對於URL長度來說,各個瀏覽器都有對應的限制,一般情況下生產下我們限制為1024是一個比較安全的值,關於這塊的內容大家可以自行搜索相關的資料進行查詢和確認。

秒級的發布與回滾

在此新架構之前,整個系統的發布和回滾是非常不可靠、緩慢且不靈活的,對於一次發布而言如果牽扯到非常多的子模塊和子系統,那麼耗時20多分鐘是非常頻繁的事情。由於新的架構是以平台化來進行設計,其希望能夠接入更多的團隊和項目產品,因此我們需要非常簡便及可靠的發布邏輯作為支撐。

由於sissis-ssr所有組件相關信息都是以依賴表為基礎,因此我們很容易在不涉及任何SSR服務下進行,其過程如下:

  1. 將對應目標產物同步至Combo服務,若失敗則直接阻斷
  2. 將對應目標產物同步至AWS S3中,若失敗則直接阻斷
  3. 修改Database的相關表中依賴表的路徑信息,失敗則直接阻斷
  4. 刷新Redis的依賴表的路徑信息,設置失效時間

同時,對於回滾來說,由於Combo服務和AWS S3對於各個時期的目標產物並不主動清除(一般保存一定數量的版本或是1-2個月的時長),因此當我們進行回滾操作時只需要將對應的依賴表信息修改至上一個版本的路徑即可。

系統安全與運行隔離

既然作為一個開放的平台化系統,那麼關於整體的系統安全也是需要考慮的一個重要問題。由於組件的開發可以被下發給其他團隊,因此其中的不可控性非常的高,況且整個組件的開發還會涉及到SSR的服務端邏輯,那麼如果寫出風險性的代碼是非常存在可能性的。試想,如果A團隊編寫的SSR端代碼內部包含了了process.exit(0),那麼導致整個進程被重啟,實際上會造成非常大的困擾。因此我們在運行過程中需要盡可能的做到運行隔離,限制SSR端代碼可調用的基礎庫,保障整個系統的安全運行。

Node實際上有非常多的沙盒實現可以使用,包括Node自身的vm模塊,以及開源的vm2Safeify等實現。但是這些實現由於都是基於JavaScript的特性來做到的,在實際中仍然有非常多可以破壞的方式,例如對於vm模塊來說,我們可以編寫如下的代碼用來退出進程:

深入淺出動態化SSR服務(三):架構篇 3

執行此代碼可以很容易發現Node相關進程直接被成功退出了。那麼還有更好的解決方案麼?實際上由於在服務端,我們可以很容易引入V8來編寫我們自己的類似Node的API子集運行環境,僅開放必要的模塊,例如http等,同時制定嚴格的約定並在編譯期進行檢查。通過這種方式,在極小影響服務性能的情況下達到了運行隔離的要求,從而解決了系統安全的問題。

自動化測試

在整個架構中,測試也是我們必須考慮的一環。由於我所在的團隊並沒有測試,因此從我們自身的需求出發,實際上是需要盡可能的降低測試的成本。在現在的前端技術中,單向數據流已經是一個非常普遍以及常用的技術方案,Vue自身也有Vuex這樣的單項數據流的管理方案。但由於可視化組件在我們自身的場景中一般比較輕量,使用類似Vuex這樣的方案未免就有點殺雞用牛刀了。除此之外,由於系統希望盡可能的開放給其他團隊,能夠讓其他團隊降低上手的成本,因此我們僅編寫了一個簡單的 Store模式 提供使用。

由於單向數據流能夠很輕易的獲得組件的前後完整狀態數據,因此其配合簡單的錄製工具以及puppeteer能夠達到很好的節約測試成本的效果。

最後的最後

通過三篇關於sissis-ssr以及整體架構的介紹,我們逐漸清晰化了一個工業級的SSR服務的大致框架。希望大家通過閱讀者三篇介紹後能夠從中獲得啟發,體會到其中前端工程化的深刻含義,並把對應的思路帶入到自己所在的項目當中,使其獲得進步與提升。

相關閱讀

https://www.infoq.cn/article/SlgQEvW8VGt8EEiTeXEd

https://www.infoq.cn/article/qLewQSiT7OshkUgw18e5