背景
H5秒開優化是一個老生常談的問題,於是得物聯合了客戶端和H5共同發力。本文將逐步介紹如何通過客戶端 + H5 的優化手段(1+1>2)把秒開從 30% 提升到 75% ? 後續介面預請求、客戶端預渲染以及預載入2.0上線後還會再次助力指標提升。
為什麼要做優化?
Global Web Performance Matters for ecommerce的報告中指出
- 47%的使用者更在乎網頁在2秒內是否完成載入。
- 52%的線上使用者認為網頁開啟速度影響到他們對網站的忠實度。
- 每慢1秒造成頁面 PV 降低11%,使用者滿意度也隨之降低降低16%。
- 近半數移動使用者因為在10秒內仍未開啟頁面從而放棄。
整體系統架構圖:
指標選擇
首先講一下得物用來衡量秒開的指標 FMP,那為什麼不選擇 FCP 或者 LCP 呢?FCP 只有要渲染就會觸發,LCP 相容性不佳,得物希望站在使用者的角度來衡量秒開這件事情,使用者從點選開啟一個webview到首屏內容完整的呈現出來的時間點就是得物定義的FMP觸發時機。
指標清楚了之後,再來看一下完整的 FMP 包含哪些耗時。
接下來將分為兩大部分進行介紹,客戶端優化部分和H5 優化部分。
客戶端優化
通過 HTML 預載入、HTML 預請求、離線包、介面預請求、連結保活、預渲染等手段提升頁面首屏開啟速度,其中預載入、預請求、離線包分別可提升10%左右的秒開。
HTML預載入
通過配置由客戶端提前下載好HTML主文件,當使用者訪問時直接使用已經下載好的HTML文件,以此減少HTML網路請求時間,從而提升網頁開啟速度。
如何確定需要下載的頁面
前人栽樹後人乘涼,得物App有很多的資源位,banner、金剛位、中通位等,這些位置顯示什麼內容,早就已經是智慧推薦演算法產出的了,那麼就可以直接指定這些資源位進行預載入。
頁面快取管理
頁面被預載入之後,總不能一直不更新吧?那麼什麼時候更新頁面的快取呢?
- 預載入的頁面存放於記憶體中,關閉App快取就會被清除
- 通過配置過期時間人為控制最大快取時間
- 在頁面進入後發起非同步執行緒去更新HTML文件。
被現實打臉:
但是在後面的灰度過程中被現實狠狠的教育了一頓,發現有些SSR的頁面會涉及到狀態的變更,比如說:領劵場景。這些狀態都是經過SSR服務渲染好的,使用者在進入頁面時還沒有領劵,這個時候去更新HTML文件,實屬更新了個寂寞,在使用者領劵之後關閉頁面再次進入,發現頁面中的狀態仍是讓使用者領劵,點選領劵又告訴人家你已經領過了。
改進措施
- H5 頁面在開啟時針對狀態可能會發生變更的元件,再次請求介面獲取最新的狀態資料。
- 客戶端由進入頁面就更新HTML文件修改為:關閉webview時更新HTML文件。
線上收益效果
至此問題也解決了,工程師的任務結束了嗎?如果你認為功能做上去就算結束,那麼此時此刻請你一定要轉變思維,想一想我們的目標是什麼?我們的目標是提升秒開,預載入只是一種提升秒開的手段,但是在功能做上去之後並不知道這個功能帶來了多少秒開的收益,因此在把功能開發完成上線之後,就要開始關注上線之後的結果,來看看預載入的效能表現如何。 從下圖可以看到,預載入開啟狀態下可提升10%以上的秒開率。
遇到的挑戰
- 預載入的頁面基本上都是 SSR 服務的頁面,預載入無形中造成了大量的請求,此時得物的SSR服務扛不住這麼大的請求量
- 即使SSR 服務扛得住,也會對後端整個服務鏈路造成壓力
SSR服務 擴容
要解決伺服器壓力問題,很自然就會想到增加機器,於是我們對SSR機器數量做了一次擴容,將機器數量提升了一倍,這個時候繼續嘗試擴大預載入的使用者數量,但是仍然無法抗住這麼大的QPS,而且此時還引發了第二個問題,演算法部門的伺服器發出了告警,於是放量計劃又一次遇到了阻礙。
破局者 CDN
利用CDN 伺服器的快取能力既可以減輕 SSR 伺服器的壓力又可以減少後端服務鏈路的壓力,這麼好的東西為什麼不用呢?這裡留個懸念,後面將H5優化部分會詳細介紹。
客戶端配合改造
- 支援針對CDN域名進行全部開放預載入能力,針對非CDN域名保持原有放量比例
開屏頁預載入
在這個過程中還分析了頁面的流量佔比,發現開屏廣告來源的頁面流量佔比也很高,那麼是不是可以把開屏廣告的HTML文件內容也給預載入下來呢?
開屏 頁面 預載入 策略
- 對預載入列表進行去重,開屏廣告列表中可能會存在重複的頁面,他們的背景圖和生效時間是不同的
- 增加了生效時間相關配置,開屏廣告列表中存在於未來某段時間才會展示的頁面
- 新增黑白名單控制,開屏廣告列表中可能會有第三方合作頁面,他們不希望預載入統計會造成PV時不準確
預載入 展望
既然可以提前下載好HTML,那是不是可以更進一步,提前把頁面內的資源載入好,這樣在開啟一個頁面的時候可以減少大部分的網路請求從而更快速的把內容呈現給使用者。這裡還需要考慮如何跟下面講到的離線包進行協作。
HTML預請求
在webview初始化同時,去請求HTML主文件,等待HTML文件下載完成 且 webview初始成功後渲染,減少使用者等待時間,客戶端請求成功後,webview 載入本地 HTML,並儲存以供下次使用。預請求HTML開啟狀態下可提升8%左右的秒開。
預請求 VS 預載入
本質上HTML預載入和HTML預請求的區別就是下載HTML文件的時機不同, 預載入是在App啟動後使用者無任何操作的情況下就會去下載,但是預請求只會在使用者單擊開啟H5頁面的時候才會去下載。如果使用者是第二次開啟某個H5頁面,此時發現本地有已經下載好的HTML且尚未過期就會直接使用,這個時候的行為表現就跟預載入的功能是一致的了。
遇到挑戰
上線之後發現預請求只提升了2%左右的秒開,經過分析發現問題:
- 快取有效時間太短,頁面過期時間只配置了10分鐘,也就是說在10分鐘之後使用者就要重新去下載一次,那能不能把快取時間延長呢?
- H5頁面是沒有自更新能力,無法支援配置更長的快取時間,跟預載入HTML問題一致。
深入挖掘
在本地用低端機對整個秒開耗時鏈路進行了分析,為什麼要用低端機分析呢?低端機有個好處,天然的加上了慢放功能,可以最大程度發現問題。
安卓 h5 頁面載入 與 原生布局填充並行執行
從圖中可以看出h5 頁面載入之前 耗時 分佈在 activityStart() 函式,該函式 包含了 onCreateView ,其中耗時最長是 佈局填充 inflate(),因為 WebView 物件是提前建立好的,直接從物件池中取出的,所以耗時主要在 初始化過程,WebView 自身的初始化 WebViewChromiumFactoryProvider. startYourEngines (耗時 87 us,不到 1 ms),耗時還有 WebView 的一些其他初始化,jockey 的初始化 等等。
而秒開的計算是包含了 View 初始化到 WebVIew url 載入 的耗時,從而發現了優化點,可以將Webview loadUrl 前置,h5 頁面載入 與 原生布局填充並行執行。在 onCreateView 時,建立 FrameLayout 進行返回,執行 WebView loadUrl 之後,主執行緒開始 對佈局進行 inflate,佈局載入成功後,將其 addView 到 FrameLayout 中,減少了 loadUrl 的阻塞時長。中高階機型有 15ms 左右優化,低端機型有 30 ~ 50 ms 優化 效果。
雙端下載HTML的時機提前至路由階段
預請求HTML時機是在進入到 native 頁面中,這個時間點距離使用者單擊事件已經過去100ms,那麼是否可以將下載HTML的時機提前呢?經過一番探索,最終選擇在路由階段進行攔截,既可以統一收口而且距離使用者點選的時間間隔可以忽略不計。通過這種方式將下載HTML時機提前了平均80ms+。
此時的流程變成了下面這樣。
可能有的同學會問了,為什麼不在使用者點選的時候去下載呢? 從使用者點選到路由肯定還是有耗時的。
- 程式碼層面不好維護,如果在點選時就需要侵入到業務層面,入口千千萬,很難進行維護管理
- 從點選到路由這部分耗時線上下進行了效能測試,幾乎可以忽略不計
最終線上收益效果
在上述問題解決後,將快取時間修改為1天,發現預請求HTML開啟狀態下可提升8%左右的秒開,已經和預載入的效果相差不大了。
離線包
通過提前將H5頁面內所需的css、js等資源聚合在一個壓縮包內,由客戶端在App啟動後進行下載解壓縮,在後續訪問H5頁面時,匹配是否有本地離線資源,從而加速頁面訪問速度。
安卓實現
資源攔截這塊安卓這邊實現比較簡單,webview支援 shouldInterceptRequest, 可以在該方法內檢測是否需要進行資源攔截,需要的話返回 WebResourceResponse 物件,不需要直接返回 null
iOS 實現
但是在iOS 這邊遇到了一些困難,調研了以下方案:
方案一:NSURLProtocol 攔截方式
NSURLProtocol 攔截方式,使用WKBrowsingContextController和registerSchemeForCustomProtocol。 通過反射的方式拿到了私有的 class/selector。通過把註冊把 http 和 https 請求交給 NSURLProtocol 處理。通過這種方式確實可以攔截請求,但是發現post請求的body會出現丟失的問題。而且NSURLProtocol一經註冊就是全域性開啟。我們希望他只會攔截接入了離線包的頁面,但是沒有辦法控制他,他會攔截所有頁面的請求,包括第三方合作頁面,顯然這是無法接受的。
方案二:通過CustomProtocol攔截請求
在iOS 11及以上系統中, 擁有了載入自定義資源的API:WKURLSchemeHandler。
可以修改當前頁面url為自定義 scheme 協議,比如:https://fast.dewu.com 修改為 duapp://fast.dewu.com 然後在客戶端內註冊該 scheme,前端配合修改頁面內所有的資源請求未自適應協議,如:src="//fast.dewu.com/xxx" 就可以實現攔截。 但是在測試過程中發現,介面為了安全起見只允許白名單內的域名發起跨域請求,且無法配置多個域名,導致該方案無法繼續進行。
方案三:hook handlesURLScheme
仍然是使用 WKURLSchemeHandler 然後通過 hook WKWebview 的 handlesURLScheme 方法來支援 http 和 https 請求的代理。通過這種方式雖然可以攔截請求了,但是遇到了以下問題:
body丟失問題
不過在 iOS 11.3 之後對這種情況做了修復處理,只有 blob 型別的資料會丟失。需要由JS來代理 fetch 和 XMLHttpRequest 的行為,在請求發起時將 body 內容通過 JSBridge 告知 native,並將請求交給客戶端進行發起,客戶端在請求完成後 callback js 方法。
Cookie 丟失、無法使用問題
通過代理 document.cookie 賦值和取值動作,交由客戶端來進行管理,但是這裡需要額外注意一點,需要做好跨域校驗,防止惡意頁面對cookie進行修改。
遇到挑戰
至此功能開發完成上線,先來一組線上收益資料,安卓開啟離線情況有有10%左右的收益,但是iOS開啟離線的反而秒開率更低。經過修復處理後iOS也可提升10%以上的秒開。
安卓 和 iOS 實現差異
經過分析對比發現,安卓的攔截動作比較輕,可以判斷是否需要攔截,不需要攔截可以交給webview自己去請求。
但是iOS這邊一旦頁面開啟攔截後,頁面中所有的http和https請求都會被攔截掉,由客戶端發起請求進行響應,無法將請求交還給webview自己去發起。
iOS 快取問題修復
頁面中的資源經過客戶端請求代理後原本第二次開啟webview本身會使用快取的記憶體,現在快取也失效了,於是只能在客戶端內實現了一套快取機制。
- 根據 http 協議來判定哪些資源可以被快取以及快取的時長
- 新增自定義的控制策略,僅允許部分型別的資源進行快取
離線包 下載錯誤率治理
從下圖可以看到離線包的下載錯誤率在 6% 左右浮動,這麼高的下載錯誤率肯定是無法接受的, 經過一系列優化手段,把離線包下載錯誤率從6%左右浮動下降至 0.3%左右浮動。
先來看下優化前的流程圖和問題點
通過埋點發現大量 unknown host 、網路請求失敗、網路連線斷開的情況。分析程式碼發現下載未做佇列控制,會同時併發下載多個離線包,從而導致多個下載任務爭搶資源的情況。針對發現的問題點做出了以下優化:
- 下載失敗新增重試機制,並可動態配置重試次數用於緩解網路請求失敗、網路連線斷開的問題。
- 新增下載任務佇列管理功能,可動態配置併發下載數量,用於緩解不同下載任務爭搶資源的問題。
- 針對弱網和無網路情況延遲到網路良好時下載。
- 離線包下載支援 httpdns,用於解決域名無法解析的情況。
下面是優化之後的流程圖:
展望:
針對離線資源是直接儲存在磁碟上的,每次訪問都會有磁碟IO耗時,經過在低端機器上做測試發現這個耗時會在 0 - 10ms 之間進行波動,後面會把記憶體合理的利用起來,通過設定記憶體上限,檔案數量上限,甚至是檔案型別,並通過 LRU 策略進行記憶體檔案的淘汰更新
介面預請求
通過客戶端發起H5頁面首屏介面請求,遠比等待客戶端頁面初始化、下載HTML、JS下載執行的時機更提前,從而節省使用者的首屏等待時間。在本地測試過程中發現介面預請求可提前100+ms,使用者也就可以更快的看到內容。
功能介紹
客戶端會在App啟動後獲取配置,儲存支援預請求的頁面地址及對應的介面資訊,在使用者開啟webview時,會並行發起對應預請求的介面,並儲存結果。當JS執行開始獲取首屏資料時,會先詢問客戶端是否已經存有對應的響應資料,如果此時已經拿到資料則無需發起請求,否則 js 也會發起介面請求並開啟競速模式。以下是整體流程圖:
配置平臺
那麼客戶端怎麼知道這個頁面需要請求什麼介面呢? 以及介面的引數是什麼呢? 那自然少不了配置平臺,它支援以下功能:
- 配置需要預載入的頁面 url並對應一個需要請求的 api url 以及引數
- 配置稽核功能,避免錯誤配置釋出上線
QA
既然有了 SSR 服務端渲染 為什麼還需要介面 預請求 的功能?
首先即使是在 SSR 的情況下,首屏內容中仍然可能有部分元件是骨架直出的,需要等待頁面渲染執行時才會去請求資料,另外還有一部分頁面是SPA的。 針對這兩種情況都能做到很好的補充。
預建連&連結保活
開啟後DNS 90分位耗時從80ms降至0ms,TCP建連90分位耗時從65ms分位耗時降為0,DNS平均耗時從55ms降為4.3ms,TCP建連平均耗時從30ms降為2.5ms。
網路請求耗時分析
通過上圖可以看到一個網路請求在經過DNS解析耗時、TCP建連耗時、SSL建連耗時階段之後才能把請求發出去,那麼是否可以節省這段時間的耗時呢?
客戶端常用的網路請求框架如OkHttp等,都能完整支援http1.1與HTTP2的功能,也就支援連線複用。瞭解了這個連線複用機制優勢,那就可以利用起來,比如在APP開屏等待的時候,就預先建立關鍵域名的連線,這樣進入相應頁面後可以更快的獲取到網路請求結果,給予使用者更好體驗。在網路環境偏差的情況下,這種預連線理論上會有更好的效果。
實現方案
可以通過對域名連結提前發起一個HEAD請求從而建立連結,網路框架會自動將連線放入連線池。並在預設無操作5分鐘後進行釋放,在五分鐘內重複執行上述動作即可一直保持連結。
另外這裡需要注意下連線池的數量問題,如果連線池的資料太小,但是域名比較多的話,通過預建連保持的連結很容易就會被釋放掉,這就需要通過域名收斂或者調大連線池的數量來進行優化。
線上收益
那預建連會不會增加伺服器的壓力呢? 這個肯定是會的,首先會針對預建連功能本身就行灰度策略,在HTML頁面通過CDN託管後,直接針對 cdn 域名進行全量開啟,從而不用擔心 cdn 域名扛不住壓力。
來看一下線上效果,通過下圖可以看到在開啟後DNS 90分位耗時從80ms降至0ms,TCP建連90分位耗時從65ms分位耗時降為0,DNS平均耗時從55ms降為4.3ms,TCP建連平均耗時從30ms降為2.5ms。
預渲染
客戶端提前通過webview將頁面渲染好,等待使用者訪問時,可直接展示。從而達到瞬開效果。但是這種功能肯定不能對所有的頁面進行開放,而且存在一定的弊端。
- 會額外消耗客戶端資源,需要在主執行緒空閒時執行,並需要控制預渲染的頁面數量。
- 如果頁面一進入就會下紅包雨,這種頁面是不適合做預渲染的,需要進行規避。
下圖【開學季】是業務上已經進行預渲染的H5頁面,可以看到在開啟【開學季】頁面時,頁面已經渲染完畢,絲毫沒有等待過程。
後面計劃把這種能力放大到通用的webview上,針對大促以及PV量高的頁面進行開放。
H5 優化
SSR服務端渲染
SSR 服務端渲染(英語:server side render)一般情況下,一個H5頁面的資料渲染完全由客戶端來完成,先通過AJAX請求到頁面資料並把相應的資料填充到模板,形成完整的頁面來呈現給使用者。而服務端渲染把資料的請求和模板的填充放在了服務端,並把渲染的完整的頁面返回給客戶端。
SSR對於秒開有平均15%的提升,既然是服務端渲染,就會對伺服器造成壓力,尤其是在預載入HTML功能開啟後,那得物是如何解決的呢?
前期優化內容:
- 介面快取:node服務向ctx中注入redis例項,業務方在服務端渲染的邏輯中處理介面相關的快取,這其中涉及:配置檔案下發、ab介面。
- 靜態頁面快取:由於頁面完成是無介面互動,並且所有使用者展示都是一樣的,由renderToHtml生成靜態html資源,寫入快取。
- 無使用者狀態頁面快取:此類頁面大多數情況下展示內容都是一樣的,服務端請求的資料也都是一致的,服務端處理的時候根據是否有使用者登陸狀態判斷是否需要快取。
- 千人千面介面內容由SSR修改為CSR,並顯示骨架圖,由於千人千面內容都是由演算法介面提供,且演算法介面本身響應較慢,因此通過這種方式可以減少服務端響應耗時,且能更快速的給使用者展示內容。
破局者CDN
通過這麼多優化手段仍然無法滿足預載入的需求,並且通過分析發現網路階段耗時較長,最終還是搬出了CDN這個大殺器,一直沒上CDN的原因有很多,主要有以下幾方面:
- 得物的頁面是千人千面的每個人看到的內容都不同
通過上述優化4即可解決,將原本SSR渲染的內容修改CSR,由於已經上了CDN了,後續計劃將這部分內容再次修改回SSR,這樣使用者可以更快的看到商品而不是骨架,然後通過 CSR 的方式來更新內容。
- 頁面狀態變更,無法及時更新快取
這個問題在上述客戶端預載入優化部分已經有解決方案了,可以通過在頁面開啟後針對有需要的元件再次請求介面重新整理資料以確保資料的準確性,但是這部分工作量也是比較大的,梳理出來的需要重新整理狀態的元件就有30+,而且之後開發的元件都需要考慮狀態更新的事情。
- HTML模板內容變更無法及時更新
引起模板內容變更的地方有兩處,第一個場景就是在搭建器場景下,運營可以動態修改模板內容導致頁面結構變更(低頻次),第二個場景是專案發版後模板內容需要更新(高頻)。
這個問題可以通過在感知到內容變更時自動呼叫CDN服務商的重新整理快取介面來更新CDN快取內容。
預渲染HTML
通過puppeteer將SPA頁面渲染出來並將HTML文件進行儲存,配合上述頁面重新整理策略,並將HTML通過CDN進行託管,讓你的 SPA 頁面 像 SSR 一樣絲滑。
主要實現方案是通過基於webpack的外掛prerender-spa-plugin,並配置需要預渲染的路由,這樣經過打包之後就會產出對應路由的頁面。方案本身是通用的,但是每接入一個頁面都需要人工check。
不起眼的css包大小優化
眾所周知 css 載入會阻塞HTML渲染,最終將首屏公共css從118kb縮減至38kb,下圖通過 chrome 模擬弱網環境下的SSR頁面載入時序圖。從圖中可以看出 styles.fb201fce.chunk.css 下載耗時 18s,阻塞了頁面的渲染,HTML 主文件耗時 2.38s 就已經下載完成了,但是實際渲染時間卻是在 20s 之後。
優化思路也比較單間,將首屏所需要的css 檔案通過內聯方式內嵌到HTML中,由SSR服務一併返回,並對css檔案進行拆分,按需載入。
思路有了,接下來就看怎麼去實現了,最初嘗試了MiniCssExtractPlugin 外掛他可以把css分成單獨的檔案,並且每一個js會對應生成一個css檔案,但是他需要建立在webpack5之上,然而專案中使用的next版本是9.5,於是就想著升級到最新版next12,升級後發現,在構建中其他包各種報錯,發現有些包並不支援最新的next12,在嘗試一天的修復之後,仍未解決,且升級到最新版不確定是否會引發其他穩定性問題,暫且擱置尋找其他方法。
經過不懈的努力,通過閱讀 next 原始碼發現了端倪,發現在打包時將所有的公共css通過 splitChunks 進行分組,由於專案中元件都是動態引入的,這裡直接在 next.config.js 中修改webpack打包引數,將 splitChunks.cacheGroups.styles 配置刪除,使用預設的 chunks: async 配置,即可實現按需引入。
圖片優化
避免圖片src為空
雖然src屬性為空字串,但瀏覽器仍然會向伺服器發起一個HTTP請求,尤其是在SSR伺服器壓力扛不住的情況下,因此這裡需要特別注意一下。
圖片壓縮以及格式選擇
WebP 的優勢體現在它具有更優的影像資料壓縮演算法,能帶來更小的圖片體積,而且擁有肉眼識別無差異的影像質量;同時具備了無損和有損的壓縮模式、Alpha 透明以及動畫的特性,在 JPEG 和 PNG 上的轉化效果都相當優秀、穩定和統一。
通過向圖片伺服器傳遞引數選擇合適的解析度
細節優化
打包優化
- 頁面元件拆分,優先載入首屏內容所需資源
- webpack splitchunks 有效拆分公共依賴,提高快取利用率
- 元件按需載入
- Tree Shaking 縮減程式碼體積
非關鍵 js 、 css 延遲載入
- defer、async、動態載入js
- iOS 裝置延遲載入 js
媒體 資源載入 優化
- 圖片、視訊懶載入
- 資源壓縮、通過向圖片伺服器傳遞引數選擇合適的解析度
其他資源優化
- 資料埋點上報延遲傳送,不阻塞 onload 事件觸發
- 自定義字型優化,使用 fontmin 生成精簡的字型包
頁面渲染優化
- 頁面渲染時間優化
<!---->
- SSR頁面首屏 css 內聯(Critial CSS)
<!---->
- 合理使用 Layers
- 佈局抖動優化:提前定好寬高
- 減少重排重繪操作
程式碼層面優化
- 耗時任務分割
<!---->
- 通過 Web Worker 減少主執行緒耗時
<!---->
- 通過 RAF 回撥,線上程空閒時執行程式碼邏輯
- 避免 css 巢狀過深
監控
為了幫助開發者更好地衡量和改進前端頁面效能,W3C效能小組引入了 Performance API ,其中Navigation Timing API 實現了自動、精準的頁面效能打點。得物前端效能監控指標也都是從 Performance API 中獲取資料進行上報統計分析的。
系統架構
SDK 資料採集完畢後,會上報到 阿里雲 sls 日誌平臺,隨後通過 flink 實時消費清洗資料後落庫至 clickhouse 中,平臺後端通過讀取 clickhouse 資料並做各種聚合處理後使用。
指標大盤
做優化之前首先要建立監控指標,網際網路稱之為抓手,沒有監控指標的情況下,任你怎麼優化,都不知道優化的效果怎麼樣,更不知道下一步該做什麼?以及還有哪些問題沒解決。因此優化之前指標先行,當然一定要指標的準確性。
指標大盤主要包含以下功能:
- 可快速檢視某段時間內的版本、裝置廠商、裝置名、裝置系統版本以及網路佔比情況,還可以根據這些欄位進行篩選排查。
- 中間區域展示了比較關注的整體以及活動頁面的客戶端耗時和H5秒開耗時的情況。
- 底部區域展示了各業務域的秒開耗時情況。
- 這裡同時展示了平均耗時和90分位耗時,平均耗時有個弊端就是很容易被平均,大家應該都有被平均的經歷。 90分位耗時這裡簡單講解一下:意思就是有90%的訪問耗時是低於90分位耗時的,以此類推 50 分位就是有50%的訪問耗時是低於50分位的,分位值是將所有的耗時資料從小到大排序後得出的。
白屏監控
在正常情況下,完成上述的優化措施後使用者基本是可以秒開 H5 頁面的了。但異常情況總是會有的,使用者的網路環境和系統環境千差萬別,甚至 WebView 也可能發生內部崩潰。當發生問題時,使用者看到的可能就直接只是一個白屏頁面了,所以進一步的優化手段就是需要去檢測是否發生白屏以及相應的應對措施。
檢測白屏最直觀的方案就是對 WebView 進行截圖,遍歷截圖的畫素點的顏色值,如果非純色的顏色點超過一定的閾值,就可以認為不是白屏。首先獲取包含 WebView 檢視的 Bitmap 物件,然後把截圖縮小到指定的解析度大小如:100*auto,遍歷檢測圖片的畫素點,當非純色的畫素點大於 5% 的時候就可以認為是非白屏的情況,但是還有很多列外的情況,我們通過圖片識別技術對截圖進行分析,可以很好的感知當前是否白屏、是不是在loading、是不是特殊頁面等。
白屏是一個重要的指標,我們針對整體白屏率快速拉昇、新增白屏頁面發出告警通知,便於開發人員及時介入開始排查問題。
效能問題發現
主要通過 CDN 未覆蓋監控、http請求監控、網路監控(載入失敗、耗時異常、傳輸大小異常)、圖片監控(未壓縮、解析度異常)等監控手段發現頁面中的潛在問題,同時還提供了問題分析能力,在問題分析頁面輸入頁面url地址即可幫助您發現問題並給出修改建議。
CDN 未覆蓋 監控
CDN的重要性不言而喻,它可以加速資源訪問速度,從而提升使用者體驗,我們通過對線上埋點資料分析,找出CDN未覆蓋的資源列表,從而推動各業務同學優化。
HTTP請求監控
為什麼要監控HTTP請求呢?我們先來看一下HTTPS相對於HTTP新增的特點:
- 內容加密:採用混合加密技術,中間者無法直接檢視明文內容
- 驗證身份:通過證書認證客戶端訪問的是自己的伺服器
- 保護資料完整性:防止傳輸的內容被中間人冒充或者篡改
那麼HTTP就容易被中間人檢視到內容,甚至被篡改,既然如此為了我們服務的安全性就需要對現有的http協議統一進行升級改造,那就需要監控去發現。
網路監控
某些頁面秒開率低,那就要分析一下原因,是不是這個頁面的介面響應比較慢呢,還是說頁面本身有請求比較大的資源? 如果發生網路請求失敗的情況也要第一時間感知,不能被動等待使用者反饋。
圖片監控
包含圖片未壓縮、圖片解析度異常、圖片傳輸大小大於 300kb 異常、動圖資源傳輸大小大於 1M 異常功能。
頁面問題分析
上面列出來一堆功能,對於業務的同學可能比較煩惱,我一個頁面具體有哪些問題呢?你不能讓我去上面的功能裡面一個個看,哪個異常是我負責的頁面的吧? 這個功能本身就行將現有的功能利用起來,通過一個頁面path進行聚合分析。
異常監控
H5異常一直是使用 sentry 進行監控的,但是sentry系統因缺乏同PV、DAU資料的關聯性,因此無法衡量產品異常發生後所帶來的嚴重程度。在業務域關聯上的缺失導致異常問題無法根據業務域進行劃分。使用者行為日誌也尚未與Native 端側打通,在問題分析時容易遇到上下文不全的瓶頸。還有一個問題是sentry會有限流措施,當qps較高時會丟棄一部分異常資料。
由於sentry已經可以幫助我們進行一定的問題排查分析能力,我們不打算做sentry同樣的功能,而是做sentry不支援的部分,針對上述問題我們設計了以下功能:
- 異常問題指標衡量
<!---->
- 增加異常率、頁面異常率、影響使用者率趨勢
- 增加問題多維度(系統版本、APP版本、H5釋出版本、網路等)下的分佈佔比、業務域劃分
<!---->
- 異常問題聚合能力增強(聚合問題能力增強)
<!---->
- 異常列表支援最新新增、Top PV、異常次數、影響使用者數排序
- 區分三方sdk異常、介面異常等不同異常型別劃分
未來展望
雖然目前秒開率已經做到了75%以上,但是同時我們還有一個重要的指標,90分位耗時,致力於提升末尾使用者H5頁面使用體驗,在90分位優化完成後,可能會考慮繼續深入優化95分位耗時。
總結
最後感謝那些為得物H5頁面秒開做出貢獻的同學,感謝H5團隊,同學們都很棒,各種優化手段和想法層出不窮。
至此我們系統的講解了背景以及從指標建立到秒開優化上線的全過程,全文分成了三個部分,客戶端、H5、以及監控。如果閱讀本文對您有所收穫,麻煩您動一動發財的小手點個贊吧! 如果閱讀完還意猶未盡或者有什麼問題和想法歡迎留言區評論交流。
最後的最後奉上整體優化腦圖:
文/XU MING
關注得物技術,做最潮技術人!