性能的重要性不言而喻,需要申明的是,美團今天不講業(yè)界最佳性能實踐,這些實踐已經有很多沉淀,具體可以參考《高性能網站》和《高性能瀏覽器網絡》等書,另外,美團不打算講性能優(yōu)化的結果指標,比如頁面完全加載時間,首屏時間,結果指標固然重要,是美團工作成果的量化衡量,但是對于做性能優(yōu)化工作的工程師來說,過程指標對其起到的幫助作用更大。
既然不講最佳實踐,那講什么呢?美團按最佳實踐提供的方法去實踐,但是后來遇到了瓶頸,到底遇到了什么瓶頸?美團是如何突破這個瓶頸的?成效如何?這些對在座的各位又有什么借鑒意義呢?
遇到什么瓶頸?
在遇到瓶頸之前,美團做了很多工作,主要包括:
簡單的數(shù)據采集,包括完全加載時間,DomReady 時間,需要注意的是這些都是結果指標;
依照“業(yè)界最佳實踐”快糙猛的做了很多事情:比如異步化,靜態(tài)化,LazyLoading,BigRender,這些實踐效果都還不錯;
因為只有結果指標數(shù)據,這個階段美團絕大部分決策都是基于別人的經驗,甚至拍腦袋,而不是基于應用的實際性能細節(jié)數(shù)據;
快糙猛的方式注定不是可持續(xù)的,很快,美團遇到了瓶頸,具體是什么瓶頸呢?
首先,如果把業(yè)界最佳實踐當成燃料,而性能優(yōu)化當成駕車遠行的話,美團的燃料很快就燒完了,因為大家總結出來的通用的優(yōu)化手段總是有限的,而美團的目標還沒有達到;
其次,因為美團只采集了結果指標,只知道整體表現(xiàn)如何,面對異常波動美團顯得特別無力,因為顯示世界影響性能的因素太多了,對于到底發(fā)生什么事情了,美團無從得知;
再次,由于對性能缺少內窺,美團無法找到更多的優(yōu)化點,實際上,美團需要一個類似于顯微鏡的東西,來看看應用內部還有哪些可優(yōu)化的地方;
如何突破瓶頸?
面對這些瓶頸,美團需要想辦法去突破它。在坐下來想辦法之前,美團往后退一步,仔細考慮這樣一個問題:美團到底在優(yōu)化什么東西?是文檔的生成速度?頁面資源的加載速度?頁面的渲染速度?或者說更高大上的用戶體驗?這些問題想清楚了,才能分析的更徹底。
其實,大多數(shù)的性能優(yōu)化工作都開始于瀑布流圖的分析,下面美團就來看看美團項目詳情頁的瀑布流圖:

美團把項目詳情頁的資源分為以下幾部分:
主文檔,即頁面的內容,在拿到主文檔之前,瀏覽器啥都干不了;
核心 CSS,和首屏圖片,在拿到這些之后,瀏覽器可以開始渲染了;
核心 JS,拿到這些內容之后,頁面的交互被豐富,但是也會阻塞;
其他內容,比如雪碧圖,統(tǒng)計腳本等;
從技術上來講,美團優(yōu)化的就是這個瀑布流圖的每個環(huán)節(jié),那么瀑布流圖的背后是什么?
其實就是頁面加載過程中各個資源的加載時間分解:從上到下的箭頭表示時間軸,從瀏覽器跳轉,緩存檢查,再到 DNS、TCP 建連,然后發(fā)起主文檔請求,再到接收完最后一個字節(jié),再到瀏覽器開始CSS、JS、圖片的下載,最后是頁面渲染和交互響應。

根據《高性能網站建設指南》上的數(shù)據以及美團的觀察,整個頁面的加載可以劃分為 3 大塊:網絡時間、后端時間、前端時間,發(fā)生在網絡和后端的時間占到整體加載時間的 10% 和 20%,而前端資源加載時間占到整體加載時間的 70% ~ 80%。
前端資源加載是否快速對性能影響是最大的,這里面資源的加載順序,并發(fā)數(shù)量,都有很多的工作可做:比如,如果你發(fā)現(xiàn) CSS 加載之前的阻塞時間很長,那很可能是資源加載順序不合理,這必然會導致瀏覽器渲染延后。
頁面的加載時間還能分解的更細么?到目前為止,美團都是站在瀏覽器的視角,劃清了各個環(huán)節(jié)。瀏覽器拿到文檔之前,是不會做任何事情的,后端響應速度的變動多數(shù)時候能引發(fā)性能上的蝴蝶效應,美團的突破口就在后端處理時間上:服務器收到請求之后,會經歷請求分發(fā)、業(yè)務邏輯處理、文檔生成這三個階段,在業(yè)務邏輯處理階段,會涉及到和數(shù)據庫、緩存以及內部服務的通信,拿到所有的數(shù)據之后,渲染模板,最后發(fā)送給瀏覽器。
對頁面加載過程中涉及到的所有環(huán)節(jié)進行分解和細化,就形成了美團的分析框架。
如何把控性能?
有了分析框架,那么如何全面的把控網站的性能呢?
基于這個框架,美團通過統(tǒng)計腳本加上必要的數(shù)據統(tǒng)計(這里的統(tǒng)計都是過程指標,只反映頁面加載過程中某個環(huán)節(jié)的健康狀況),就能獲得對整個網站的很多內窺。
具體來說,美團對數(shù)據的要求是這樣的:整個流程各環(huán)節(jié)的,多維度(比如分頁面、分地理區(qū)域、分瀏覽器)的,實時的(方便美團快速實驗)。所有的數(shù)據都必須是能夠反映整體的統(tǒng)計量。
而對于統(tǒng)計腳本,需要滿足兩個條件:
避免對業(yè)務代碼的入侵;
不影響被測量的頁面的性能;
針對第 1 個要求,需要開發(fā)獨立的統(tǒng)計腳本,避免其與現(xiàn)有的框架耦合,方便移植到其他項目;而針對第 2 個要求,需要在主文檔加載完畢之后,再注入統(tǒng)計腳本收集數(shù)據,并且盡可能的合并數(shù)據請求,減少帶寬消耗。
確定了數(shù)據統(tǒng)計腳本的約束條件之后,美團從哪里得到這些數(shù)據呢?目前使用的主要途徑有:
主文檔加載速度,利用 Navigation Timing API 取得;
靜態(tài)資源加載速度,利用 Resource Timing API 取得;
首次渲染速度,IE 下用 msFirstPaint 取得,Chrome 下利用 loadTimes 取得,美團的 Chrome 瀏覽器用戶占比超過 70%;
文檔生成速度,則是在后端應用內打點來獲得;
對于主文檔加載速度,美團從宏觀到微觀的做了這樣的分解,從上到下的時間流,右邊的時刻標記了每個指標從哪里開始計算到哪里截止,比如,跳轉時間 redirect 由 redirectEnd - redirectStart 計算得到,其他的類推:

采集主文檔加載速度的具體做法是:
在主文檔 load 之前提供可緩存數(shù)據的接口,方便在統(tǒng)計腳本載入前就可以準備數(shù)據;
在主文檔 load 之后注入數(shù)據收集腳本,該腳本加載完成之后會處理所有的數(shù)據;
利用 Navigation Timing API 收集計算得到上圖中的指標;
給所有數(shù)據打上頁面、地理位置、瀏覽器等標簽,方便更細維度的分析;
對于靜態(tài)資源的加載速度,美團也做了類似的分解和采集:

需要特別提示的是,如果你使用 CDN 的話,需要讓 CDN 服務商加上 Timing-Allow-Origin 的響應頭,才能拿到靜態(tài)資源的數(shù)據。
而對于主文檔生成速度,美團則開發(fā)了性能統(tǒng)計的 Library,在框架級別集成后端性能的時間指標。
實際效果如何?
通過上面的各種數(shù)據采集,美團拿到了頁面加載全流程、全方位、多角度的真實用戶數(shù)據,有這些數(shù)據之后,美團能做什么呢?之前遇到的瓶頸不再是瓶頸,因為美團可以利用這些數(shù)據做很多事情,下面舉幾個實際的例子:
Flush Early 是否有效?
《高性能網站進階指南》上提到要盡快輸出文檔的第首字節(jié)提高性能,美團很早的時候做了這個事情,但是從數(shù)據上看,在頁面完全加載時間上的收益不大,做了更細的數(shù)據采集之后,美團快速的在線上做了這樣的實驗:在特定頁面把 Flush Early 關掉,結果發(fā)現(xiàn),瀏覽器接收到第 1 個字節(jié)的時間增加了 100+ms,如下圖(紅色箭頭表示變更上線時間點):

而完成文檔傳輸?shù)臅r間減少了 150+ms,如下圖:

表面上看,似乎禁用 Flush Early 效果好些,但是再看看瀏覽器的首次渲染時間,增加了 300+ms,如下圖:

也就是說,有些優(yōu)化措施,總結果指標上看貌似沒啥效果,但是換個角度看效果非常明顯。有了全方位的數(shù)據,美團能更高效的試錯。
發(fā)現(xiàn)新的優(yōu)化點
為了優(yōu)化文檔生成速度,美團一度想到優(yōu)化函數(shù)級別的調用,利用 FaceBook 的 HipHop 為 PHP 加速,通過數(shù)據發(fā)現(xiàn),在美團生成文檔的時間構成中 30 %是在和緩存交互,這個比例太高了,當優(yōu)化緩存服務器之后,后端時間大幅下降,緩存占比降到 10% 以下。
另外,美團主站的迭代速度非常快,每天大概 50 次左右的上線,通過數(shù)據發(fā)現(xiàn),每次上線都會導致性能的輕微惡化,如果某天上線次數(shù)越多,那么性能就好不到哪里去?原因美團合并了大量的 JS 請求,當其中的某個模塊在某次迭代中被修改,整個合并的文件需要被重新下載,這就對模塊拆分和加載提出了更高的要求。
有了更細節(jié)的數(shù)據美團能有效發(fā)現(xiàn)新的優(yōu)化點。
性能監(jiān)控平臺
美團不光突破了之前遇到的瓶頸,實際上,美團走的更遠,因為美團覺得解決一個問題不如解決一類問題,美團解決問題的思路和工具同樣能適用于公司的其他產品線:于是美團在做性能優(yōu)化的過程中逐步建設起來性能監(jiān)控平臺,目的是為公司的其他產品線和內部系統(tǒng)提供一站式的性能數(shù)據收集、計算、存儲和展示服務。

目前性能監(jiān)控平臺已經接入 20 余個公司內部系統(tǒng),能夠支持任意指標、任意維度的實時數(shù)據查詢。該平臺為不同的項目提供了性能儀表盤功能,方便快速了解整體的性能狀況:

同時還為做性能優(yōu)化的工程師提供了簡單的數(shù)據分析功能,方便其以數(shù)據驅動的方式的開展性能優(yōu)化工作:

總結
以上,就是美團做性能優(yōu)化時遇到的問題,以及解決的辦法,下面大概說下,我對這些事情的總結:
首先,需要深入的剖析問題,性能分析問題的框架,讓很多死角暴露無疑;
其次,在性能優(yōu)化這件事情上,只關注結果指標是不會給你多大幫助的,如果想真的優(yōu)化,你需要測量過程指標,從過程指標發(fā)現(xiàn)更多;
再次,解決一個問題比如解決一類問題,解決問題的思路和工具可以沉淀下來,服務更多的團隊和同事;