1. 程式人生 > >【第1145期】打造高可靠與高效能的React同構解決方案

【第1145期】打造高可靠與高效能的React同構解決方案

前言

本文為第12屆D2前端技術論壇《打造高可靠與高效能的React同構解決方案》分享內容,已經過資料脫敏處理。今日早讀文章由阿里@六猴投稿分享。

@六猴, 來自阿里國際UED體驗技術部前端專家,beidou 同構框架負責人

正文從這開始~

隨著React的興起, 結合Node直出的效能優勢和React的元件化,React同構已然成為趨勢之一。享受技術福利的同時,直面技術挑戰,在複雜場景下,挑戰10倍以上極致的效能優化。

什麼是同構?

一套程式碼既可以在服務端執行又可以在客戶端執行,這就是同構應用。簡而言之, 就是服務端直出和客戶端渲染的組合, 能夠充分結合兩者的優勢,並有效避免兩者的不足。

為什麼同構?

  • 效能: 通過Node直出, 將傳統的三次序列http請求簡化成一次http請求,降低首屏渲染時間

  • SEO: 服務端渲染對搜尋引擎的爬取有著天然的優勢,雖然阿里電商體系對SEO需求並不強,但隨著國際化的推進, 越來越多的國際業務加入阿里大家庭,很多的業務依賴Google等搜尋引擎的流量匯入,比如Lazada.

  • 相容性: 部分展示類頁面能夠有效規避客戶端相容性問題,比如白屏。

效能資料

效能是一個綜合性的問題, 不能簡單地斷言同構應用一定比非同構應用效能好,只能說合適的場景加上合理的運用,同構應用確實能帶來一定的效能提升, 先來看一個線上的案例。

0?wx_fmt=gif
isomorphic

通常來說,網路狀況越差,同構的優勢越明顯,下圖是在不同網路狀況下首屏渲染時間的一組對比

0?wx_fmt=png
isomorphic

線上案例

近兩年,無論是業界還是阿里內部都湧現了大量同構實踐, 業界比較有影響力的包括Facebook, Quora, Medium, Twitter, Airbnb, Walmart、手Q以及QQ興趣部落等

阿里內部也有大量的應用,僅列舉部分beidou開發組做過技術支援的專案

  • 阿里雲 - 大資料地產

  • 釘釘 - 企業主頁

  • 釘釘 - 釘釘日誌和審批模板市場

  • 菜鳥 - 物流大市場

  • 雲零售 - 店掌櫃

  • Lazada - PDP

  • 國際事業部 - AGLA

  • AILab - 行業解決方案

  • AILab - 智慧硬體平臺

  • AILab - AliGenie開放平臺

  • AILab - AR官網

  • ICBU - ICBU店鋪

  • 業務平臺 - 門店評價

  • 國際UED - 資料運營

  • 國際UED - 知之

  • 國際UED - 探花

  • 國際UED - Nuke官網及過程管理

  • 國際UED - 會議記錄,實時翻譯

  • 國際UED - LBS資料地圖

  • 國際UED - 數探

  • 國際UED - 微策

  • 國際UED - shuttle

  • 國際UED - fie portal

業界生態

  • react-server: React服務端渲染框架

  • next.js: 輕量級的同構框架

  • beidou: 阿里自己的同構框架,基於eggjs, 定位是企業級同構框架

除了開源框架,底層方面React16重構了SSR, react-router提供了更加友好的SSR支援等等, 從某種程度上來說,同構也是一種趨勢,至少是方向之一。

思考 與 實現

同構的出發點不是 “為了做同構,所以做了”, 而是迴歸業務,去解決業務場景中SEO、首屏效能、使用者體驗 等問題,驅動我們去尋找可用的解決方案。在這樣的場景下,除了同構本身,我們還需要考慮的是:

  • 高效能的 Node Server

  • 可靠的 同構渲染服務

  • 可控的 運維成本

  • 可複用的 解決方案

簡單歸納就是, 我們需要一個 企業級的同構渲染解決方案。

我們是怎麼做的?

基於 eggjs 加入可拔插的同構能力

  • beidou-plugin-react
    作為原有MVC架構中, view 層的替換, 使用 React 元件作為檢視層模板, 可以直接渲染 React Component 並輸出給客戶端

  • beidou-plugin-webpack
    整合 Webpack 到框架中, 在開發階段, 提供程式碼的編譯和打包服務

  • beidou-plugin-isomorphic
    服務端的 React 執行時: babel-register
    polyfill 注入: 環境變數, BOM等
    非js檔案解析: css, images, fonts…

  • 服務端支援css modules

  • 自動路由: 純靜態頁面無需編寫任何服務端程式碼,像寫純前端頁面一樣簡單

0?wx_fmt=png

這裡不再贅述具體如何實現,有興趣的讀者可以閱讀我們的開源同構框架beidou—https://github.com/alibaba/beidou

熱點問題

任何一種技術都有其適用場景和侷限性, 同構也不例外,以下試舉一二,以做拋磚引玉.

  • 記憶體洩漏

  • 效能瓶頸

記憶體洩漏不是同構應用所特有的,理論上所有服務端應用都可能記憶體洩漏,但同構應用是“高危群體”, 具體如何解決請參考本人的《Node應用記憶體洩漏分析方法論與實戰》, 接下來重點剖析下效能優化。

極致的效能優化

前面也提到了,同構應用並不一定就比非同構應用效能好,影響效能的因素實在太多了,再來看一組資料

0?wx_fmt=png
react15 performance

上圖是基於Node v8.9.1 和[email protected], 開4個程序採集到的資料, X軸是最終生成頁面節點數量,Y軸紅色的線表示RT(包括渲染時間和網路時間), 綠色的柱子表示QPS. 可以看出來:

  • 隨著頁面節點的增多渲染時間可能變得很長,QPS下降非常迅速。在頁面節點超過3000左右的時候,QPS接近個位數了,而且實際頁面中可能包含較複雜的邏輯以及不友好的寫法,情況可能會更糟。

順帶提一下, 筆者取樣了淘寶首頁和淘寶某詳情頁以及Lazada某詳情頁,頁面節點數分別是2620、2467和3701. 大部分情況下,頁面節點數低於1000, 比如菜鳥物流市場首頁看起來內容不少,其實節點數是775.

那針對3000節點以上的頁面,我們該怎麼做呢?筆者總結了以下策略並重點闡述其中一兩點:

  • 採用編譯後的React版本: 根據Sasha Aickin的部落格,React15在Node4、Node6、Node8下,採用編譯後的版本效能相比未編譯版本分別提升了2.36倍、3倍、3.85倍

  • 模組拆分: 模組拆分有利於併發渲染,目前ICBU店鋪裝修採用的就是這種方式

  • 模組級別快取: 頁面中某些模組其實是很適合快取的,比如Lazada詳情頁中節點數雖然高達3701, 但其實頁頭部分就佔比55.5%,頁尾佔比3.5%,而頁頭頁尾是常年不變的.

  • 元件級快取: 最小粒度的快取單位了,效能提升依賴於快取的範圍和命中率,運用得當,可能帶來非常大的效能提升。參考walmartlabs

  • 採用hsf代替http對外提供服務: hsf的網路消耗遠低於http, 在店鋪同構實踐中,改用hsf, java端呼叫Node端的耗時縮短了一半.

  • 部分模組客戶端渲染(對SEO無用的部分): 直接降低SSR部分的複雜度

  • 智慧降級: 當流量暴增,接近或超過閾值時,會直接導致服務的RT快速上升。可以實時監測CPU和記憶體的使用率,超過一定的比例自動降級為客戶端渲染,降低服務端壓力,CPU和記憶體恢復常態時,自動切回服務端渲染。

  • 採用Node8: 同樣在店鋪實踐中,採用Node8相比Node6, 渲染時間從28ms降低到了18ms, 提升幅度為36%.

  • 採用最新版React16:facebook官方資料, 在Node8下,React16相比編譯後的react15仍有3.8倍提升,相比未編譯的React15更是有數量級的提升。

元件級快取

如果說效能優化有”萬能”的招式,那一定是快取, 從Nigix快取到模組級快取到元件級快取,其中最讓人興奮的就是元件級快取,讓我們一起來看看如何實現

  • 攔截React的渲染邏輯,業界主要有三種實現方式

  • Fork一份React, 暴力加入快取邏輯, 代表庫是react-dom-stream, 雖然這個庫的人氣很高,但筆者還是反對這種實現方式的。

  • 通過require hook攔截instantiateReactComponent的載入並注入快取邏輯,參考react-ssr-optimization

  • 擴充套件ReactCompositeComponent的mountComponent方法,參考electrode-react-ssr-cachin

  • 注入快取邏輯, 程式碼如下

    constReactCompositeComponent=require("react/lib/ReactCompositeComponent");ReactCompositeComponent.Mixin._mountComponent=ReactCompositeComponent.Mixin.mountComponent;ReactCompositeComponent.Mixin.mountComponent=function(rootID, transaction, context){  
       consthashKey
    =generateHashKey(this._currentElement.props);if(cacheStorage.hasEntry(hashKey)){// 命中快取則直接返回快取結果returncacheStorage.getEntry(hashKey);}else{// 若未命中,則呼叫react的mountComponent渲染元件,並快取結果consthtml=this._mountComponent(rootID, transaction, context);
         cacheStorage
    .addEntry(hashKey, html);return html;}};
  • 設定最大快取和快取更新策略

    lruCacheSettings:{
         max
:500,// The maximum size of the cache
         maxAge
:1000*5// The maximum age in milliseconds}

上述快取邏輯是基於屬性的,能覆蓋大部分的應用場景,但有一個要求,屬性值必須可列舉且可選項很少. 請看下面的場景。

0?wx_fmt=png
items

淘寶某頁面上有大量的商品,而淘寶的商品又何止百萬,就算某個被快取,下次被命中的可能性依然微乎其微。那如何解決這個問題?聰明的讀者可能已經看出來了,雖然每個商品最終渲染的結果千變萬化,但結構始終是一致的,因此結構是可以快取的。

0?wx_fmt=png
template

要實現結構的快取,需要在上述邏輯上額外新增三步。

  • 生成中間結構:

  • 以元件<Price>${price}</Price>為例,將變數price以佔位符${price}代替set(price, "${price}"), 再呼叫react原生的mountComponent方法則可以生成中間結構<div>${price}</div

  • 快取中間結構

  • 生成最終元件

以上就是元件級快取的實現方式, 特別要提醒的是快取是把雙刃劍,運用不當可能會引發記憶體洩漏以及資料的不一致。

React16 SSR

  • FB在9.26釋出了React16正式版,之前萬眾期待的SSR效能提升沒有讓大家失望, 引用React核心開發Sasha Aickin的對比圖

    0?wx_fmt=png
    react16

筆者拿之前的應用升級到React16, 對比下3909節點,RT從295ms降到了51ms, QPS從9提升到了44, 提升非常明顯。

0?wx_fmt=png
react16

實戰

接下來通過一個例子,展示如何一步步地提升效能。程式碼倉庫—https://github.com/alibaba/beidou/

10倍以上效能提升

首先構造一個非常複雜的頁面, 頁面節點數是3342, 對比之下,淘寶首頁首屏的頁面節點數是831, 非同步充分載入之後(懶載入完成),整個頁面節點數為3049. 注: 淘寶頁面為動態頁面,每次取樣可能會有差異。

0?wx_fmt=jpeg
複雜頁面
0?wx_fmt=jpeg
淘寶首屏
0?wx_fmt=jpeg
淘寶全屏

初始平均渲染時間為295.75ms(Node6.92, React15.6.2)

注: 圖中有296.50ms,317.25ms,297.25ms,295.75ms四個平均值,是因為開啟了四個程序,取樣最後一個,下同。

0?wx_fmt=jpeg
初始渲染時間

啟用babel效能加速外掛, 平均渲染時間為219.00ms

0?wx_fmt=jpeg
babel效能加速外掛

採用Node8.9.1(或更新版本)平均渲染時間為207ms

0?wx_fmt=jpeg
Node8

採用production模式平均渲染時間為81.75ms

0?wx_fmt=jpeg

production mode

部分內容客戶端渲染,平均渲染時間為44.63ms

0?wx_fmt=jpeg
part csr

部分內容元件級別cache,平均渲染時間為22.65ms

0?wx_fmt=jpeg
part cache

採用React16(或更新版本),平均渲染時間為5.17ms

0?wx_fmt=jpeg
react16

結合React16和部分客戶端渲染,平均渲染時間為2.68ms

0?wx_fmt=jpeg

react16+csr

至此,服務端渲染時間已經最初的295.75ms降低到了2.68ms,提升了超過100倍。

更多效能策略

其實除了上述應用的策略,還有其它的策略,比如

  • 採用Async, 有資料稱效能提升30%, 筆者試了下,未見明顯提升。應該是經過了babel的編譯,最終沒有發揮出Async的優勢,這是因為beidou框架在服務端要支援import等ES6的寫法以及支援React的JSX語法。其實也非常簡單,直接縮小babel的編譯範圍,在beidou框架中是可以自己定義的。

  • 降低React元件的巢狀層級。試驗資料,同樣的頁面節點數,服務端渲染時間和元件的巢狀層級是線性正相關的。

  • 熱點快取

萬變不離其宗

借用《功夫》中的一句經典臺詞天下武功,無堅不破,唯快不破,同樣的,
隨著時間的推移,上面這些策略策略遲早會被破,比如react16 ssr重構之後,之前的元件級別快取邏輯不再有效。
另外,可能由於架構設計/技術選型根本就使不上勁,比如react16是今年9月26才正式發版,很多第三方元件還沒來得及升級,如果應用中有些元件強依賴於react15或者更早的版本,可能根本就沒法利用react16的效能優勢。

那麼有沒有一種萬能的辦法,能夠做到唯快不破呢?

答案是: 有的。 只有掌握了方法論,才能在不斷變化中,找到適合自己應用的效能優化策略。

具體的方法論,請參考本人的另外一篇文章《唯快不破,讓nodejs再快一點》

另:本次所有分享主題的PPT均可在官網上下載了。

最後,為你推薦

關於本文
作者:Holden
原文:https://github.com/alibaba/beidou/blob/master/packages/beidou-docs/articles/high-performance-isomorphic-app.md

0?wx_fmt=jpeg

相關推薦

1145打造可靠高效能React解決方案

前言本文為第12屆D2前端技術論壇《打造高可靠與高效能的React同構解決方案》分享內容,已經過

前端特效03|果汁混合效果-上

前幾天有一個熱心的小夥伴找到我說,我看到一個很好看的效果,可不可以講講? 這種要求我向來是不會拒絕的,於是就有了今天的案例。同樣,大家以後如果有什麼想要了解的效果的話,也可以在下方留言給我,說不定我的下一個視訊講解就是你想要的案例哦...   先上圖看看卡哇伊的設計: 當然光看靜圖沒什麼

玩轉 React 03:邂逅 React 元件

上期回顧 前文我們講解了 React 模板 JSX,接著我們繼續來看看 React 元件又是如何工作的呢? 元件化開發到了今天已經是大家的共識,在 React 中,元件同樣也是組成我們整個專案的基本單元。 react 中元件可以將UI切分成一些的獨立的、可複用的部件。元件

前端特效02|多功能提交按鈕

話說提交按鈕可能是我們工作生活中最常見的東西了,比如我現在在寫這篇文章,下面就有一個大大的按鈕,甭管上面寫的什麼文字,我知道點了那個按鈕,我這篇文章就算是發出去了。所以按鈕的作用可見一斑。 但是一般的按鈕是很難滿足我們所有的需求的,或者說很難滿足使用者的心理預期的。 從設計的角度出

1159CSS預載入Preload

前言看天氣預報,今天好多地方都開始下雪了。今日早讀文章由@李斌分享。正文從這開始~Preload

1165H5動畫:軌跡移動

前言前一段時間被各種H5總結刷屏,除了內容以及設計之外在動畫方面在其表現方面也起到了很大的加持作

1139基於Gitlab CI搭建持續整合環境

前言本文是在12月12號迅雷@趙兵在前端早讀課第三期Live中提到的關於CI構建的,可能這部分在

1182Canvas or SVG?一張好圖,兩手準備,就在 ECharts 4.0

前言在百度的 ECharts 團隊釋出了時隔兩年的大版本 ECharts 4.0 後,準備將專案

1166Ant Design 3.0 背後的故事(含演講視訊)

前言鑑於最近有兩個產品用到了這個Ant Design,本著好奇心就想了解這款產品的故事,可真是湊

Git之窗(十)“遠端倉庫”版本回退解決方案

由於TGB的圖書管理系統託管在本地伺服器上的GitLab中,最近因為大家使用Git過程出了一些問題,導致需要回退伺服器上的遠端版本,回退過程中又出了一些問題,比如push不上去,比如別人電腦的版本比遠端版本高几個commit版本等,在網上找了好多資料,這位博主的文章簡直涵蓋了我的問題,又有對

運維心得查得到資料頁面卻不顯示的解決方案

今天工作中碰到一個詭異的問題,如下圖,明明查出來是有31條資料,但是頁面上缺沒有顯示? 一開始認為是js的問題,前臺介面程式碼過濾了一遍,結果發現很簡單,沒有發現什麼問題,如下: sortOrder: 'desc', pagination: true, pageNum

linux工具使用無法使用ssh 登入ubuntu16.04的問題解決方案(root賬戶)

1.首先使用sudo apt-get install ssh 安裝最新的ssh服務。 2.windows的ssh client 的版本是3.2.9 啟動ssh服務後,採用client客戶端登入(登入賬號為root賬戶)虛擬機器出現(algorithm negotiation

Spark調優小表join大表數據傾斜解決方案

htm strong 函數 ref 通過 拉取 使用 就是 logs 【使用場景】     對RDD使用join類操作,或者是在Spark SQL中使用join語句時,而且join操作中的一個RDD或表的數據量比較小(例如幾百MB或者1~2GB),比較適用此方案。。

社區問答三十八編寫質量C程序代碼

lds smi c89 use amp href dff c程序 dbf NOJ的一道題 求助大神 Java爬蟲,信息抓取的實現 C++實現給多個變量傳值 指針定義成全局和定義在main中為什麽不一樣?定義在main中執行中止 cqj慰膛哦http://p.baidu.co

異周話題 16 2018年,你最期待的前端開發技術有哪些?

pes 社區 發展 col roc ava 前端技術 試用 帶來 【異周話題 第 16 期】2018年,你最期待的前端開發技術有哪些?話題背景2017年已經過去,2018年如約而至。2017年在前端領域裏,react已經逐漸占據主流,Angular 發布了 v4 以及 v5

異周話題 20 三年後,人工智能將徹底改變前端開發?

人工智能【異周話題 第 20 期】三年後,人工智能將徹底改變前端開發? 話題背景 去年,阿裏阿裏正式公開了人工智能設計系統“魯班”,到去年雙 11,魯班的水平已經達到了阿裏內部 P6 水準,一天能夠制作 4,000 萬張海報,平均每秒可設計 8,000張海報,並且每張海報會根據商品圖像特征專門設計,換句話說,

安富萊專題教程5工程調試利器RTT實時數據傳輸組件,替代串口調試,速度飛快,可以在中斷和多任務中隨意調用

中斷 現在 ron borde AS get bubuko 串口調試 需要 說明:1、串口作為經典的調試方式已經存在好多年了,缺點是需要一個專門的硬件接口。現在有了SEGGER的RTT(已經發布有幾年了),無需占用系統額外的硬件資源,而且速度超快,是替代串口調試的絕佳方式。

安富萊專題教程6SEGGER的J-Scope波形上位機軟件,RTT模式波形上傳速度可狂飆到500KB/S左右

static str 數值 height size oat 工作方式 調整 設置 說明:1、在實際項目中,很多時候,我們需要將傳感器或者ADC的數值以波形的形式顯示。通常的解決辦法是用串口上位機,USB接口上位機或者MDK的邏輯分析儀功能,使用這三種方式都比較繁瑣。本期專題

安富萊專題教程2uC/Probe簡易使用說明,含MDK和IAR,支持F103,F407和F429開發板

tar 微軟 OS 當前 TP ucos ima 分享圖片 nbsp 說明:1. 在uCOS工程調試時,這個軟件還是非常給力的,方便查看各種信息,可以認為是MDK或者IAR調試功能的圖形化版本,使用JLINK連接可以隨時連接查看,無需目標端代碼。2. 當前教程中,我們使用的

直播預告|芯咖秀?MCU熱的前因後果以及未來到底如何發展!

董事長 處理 ram 制造 咖啡 讓我 應對 學習 下午 何謂MCU?我相信從事電子行業的都很清楚,MCU本質為一片單片機,它將計算機的CPU、RAM、ROM、定時計數器和多種I/O接口集成在一片芯片上組成的芯片級的計算機。它存在於我們每天接觸到得各種家電、數碼產品、辦公設