本文曾發表於2013年4月的《程序員》雜誌前端
近年來,隨着用戶數和PV的增長,淘寶網的後端服務器數量增加很快;而且咱們知道,Web頁面延遲時間和轉化率之間有着直接的關聯。出於提高系統吞吐量、下降成本、減小頁面延遲、提高用戶瀏覽體驗、提升交易轉化率的考慮,淘寶網在性能優化領域作了不少嘗試。本文將從應用性能分析、基礎設施優化、應用自身優化、前端性能優化這四個方面,對淘寶網的優化嘗試作一個總結。ios
應用性能分析程序員
1.前臺應用介紹web
淘寶網前臺應用是指商品詳情、店鋪、購物車等買家直接能夠看到和使用的應用,這類應用PV較高,服務器數量較多。從技術實現來講,淘寶前臺應用都使用Velocity模板引擎渲染HTML,頁面平均大小大於100KB,WebServer不保存數據,數據來自於後端的DB、RPC服務、消息中間件、Tair、SearchEngine、TFS等外部系統,除了寫日誌、讀取配置和共通模板,磁盤讀寫不多,而相對於後端系統來講可處理的最大吞吐量較低,單臺虛擬機平均TPS不到200。根據分析,這些應用都屬於CPU密集型應用。編程
2.度量關鍵指標後端
優化工做開始前,要先給系統作次體檢,拿到關鍵指標,而後針對關鍵指標進行優化,這樣在優化工做完成後,更容易度量成果。關鍵指標有吞吐量、頁面大小、響應時間和每請求內存數。
A.吞吐量
經過線上環境單機壓測,能夠獲得服務器預設最大負載狀況下,應用單機的最高真實吞吐量。線上壓測的方法有兩種:對於全部HTTP請求都具有冪等性的系統,可使用開源的AB、http_load等工具,回放前一天的流量給服務器,逐漸增長壓力,當系統負載達到預設值時就獲得了應用當前最高吞吐量;對於全部HTTP請求不徹底具有冪等性的系統,能夠採用Nginx引流的方式,將其餘服務器的流量引到同一臺服務器,以達到增長負載、獲得最高吞吐量的目的。
B.頁面大小、響應時間
頁面大小和HTTP請求響應時間能夠經過分析服務器訪問日誌獲得。
C.每請求內存數
淘寶前臺應用都是Java應用,內存使用較多,垃圾回收很容易成爲瓶頸,因此設定這一指標來衡量應用的內存使用狀況。每請求內存數的計算方法是:JVM單位時間內申請的內存/服務器單位時間內處理的請求數。
數組
3.查找應用瓶頸瀏覽器
瓶頸是系統中比較慢的部分,在瓶頸完成前其餘部分須要等待,因此優化工做能夠從分析瓶頸開始;應用代碼的執行也符合2/8原則,即20%的代碼執行會消耗80%的資源,找到這20%的代碼去作優化纔會有效果。自底向上,查找應用瓶頸能夠分爲下面幾個部分:
A.系統瓶頸
使用top、sar、vmstat、mpstat、iostat等系統工具、JDK源生工具去分析CPU、IO、Memory在壓測時的表現,關注當前進程的Thread、鎖、打開File數、Socket數、GC表現等狀況,看哪一塊存在問題或者會先成爲瓶頸;對於關鍵代碼,使用Perf等工具查看熱點和CPU緩存命中率。通過分析,CPU計算的通用瓶頸在字符串的查找、拼接、替換,字符字節的編碼、解碼轉換,壓縮、解壓縮操做,外部調用的瓶頸在IO開銷、序列化和反序列化操做。
B.代碼瓶頸
對於運行態的Java代碼,業界有不少工具能夠用來查找瓶頸,好比收費的JProfiler、YourKit等,免費的TPTP、NetBeansProfiler、VisualVM等,咱們使用淘寶開發的適合線上運行的TProfiler工具(已開源),同時支持剖析和採樣兩種方式,能夠獲得對象建立排行和Java代碼執行次數、執行時間排行。排在前邊的熱點代碼,極有可能就是代碼瓶頸所在,以下表所示:緩存
方法信息 | 執行時間 | 執行次數 | 總時間 |
com/xxx/web/core/NewList:execute() | 61 | 3102 | 190067 |
com/xxx/web/core/PerformScreen:performScreen() | 18 | 4822 | 87822 |
com/xxx/core/DefaultSearchAuction:doMultiSearch() | 43 | 708 | 30357 |
com/xxx/core/DefaultSearchCatManager:doSearch() | 4 | 1248 | 4552 |
C.模塊瓶頸
基於前邊的熱點代碼排行數據按模塊作歸類統計,能夠得出每個模塊的CPU資源消耗比重,以下圖所示:性能優化
這樣就能夠得出:Velocity模板引擎是此應用的瓶頸模塊,須要着重優化。
基礎設施優化
1.軟件升級
淘寶以前的Web應用構建在Apache+mod_jk+JBoss4之上,軟件棧相對陳舊,新版本的特性和優化也沒法利用。作了一次大的升級後,變成如今的Tengine+Tomcat7,在一些應用上實測吞吐量有近10%的提升,也驗證了Nginx使用epollIO模型帶來的優越性能。操做系統由原來的32位升級爲64位,可識別的內存變大,增長內存後調大新生代堆大小4倍,某應用吞吐量提高達到70%,可見新生代大小對應用吞吐量很是的重要;淘寶有專門的JVM團隊和Linux內核團隊,使用taobao-jdk(補丁開源)、淘寶內核、開啓JVM大內存頁後實測,某應用吞吐量提高40%;TCP初始擁塞窗口調優,對用戶平均下載時間也有不錯的提高。在當前開源軟件百花齊放的形勢下,升級基礎軟件投入不大,卻能給系統性能帶來較大提高,很是划算。惟一要面對的問題是升級週期會比較長,由於線上環境須要長時間的beta以保證新軟件的穩定。
2.JVM調優
根據前面的分析和實踐,吞吐量與GC有直接的關係,在頁面大小不變的狀況下,調大新生代有益於提高吞吐量,減少頁面大小(每請求內存數)也能提高吞吐量。目前JDK7已經發布,但G1垃圾回收器還在開發中,通過咱們測試在GC表現上G1沒有比CMS更好,因此目前仍是選擇響應時間優先的CMS垃圾回收器。咱們的JVM部分行爲參數和性能參數以下:
-Xms4g-Xmx4g-XX:PermSize=256m-XX:MaxPermSize=256m-Xmn2000m-XX:SurvivorRatio=10-XX:+UseConcMarkSweepGC-XX:+UseCMSCompactAtFullCollection-XX:+CMSParallelRemarkEnabled-XX:+CMSPermGenSweepingEnabled-XX:+CMSClassUnloadingEnabled-XX:+UseCMSInitiatingOccupancyOnly-XX:CMSInitiatingOccupancyFraction=82
除了基本的配置還能夠作一些參數調優,好比在6u23以後默認開啓的壓縮指針,隨着JDK7發佈帶來的分層編譯、大內存頁、逃逸分析等很是值得嘗試的優化。除了參數調優,應用代碼自己也能夠調整,使其對GC更友好。在CMS垃圾回收機制下,MinorGC時業務線程會暫停25ms左右,MajorGC時業務線程會暫停500ms左右。用戶的請求被暫停500ms是不能接受的,因此優化原則就是減小MajorGC,也就是減小Young區晉升到Tenured區的對象數。能夠經過JVM源生工具jstat觀察JVM各個分區間對象的遷移狀況,而後合理分配堆每個分區的大小、調整TenuringThreshold閥值。應用對象管理要儘量縮短對象生命週期或儘量少建立新對象,減小頁面模板大小也是一個可行的辦法。咱們開發了TBJMap工具(已開源),能夠分析JVM堆每個分區裏都有哪些內容,這對於優化應用代碼很是具備參考價值。JVM性能表現的最佳狀態是沒有MajorGC,在淘寶有些應用已經作到了這點。
3.二方包優化
每個工程都依賴不少jar包,這些jar包若是用的比較頻繁對性能的影響相當重要,對二方包的優化不會隨着業務代碼的改變而性能降低,能夠說一次優化永久受益。二方包優化有兩個建議:能夠作一次的工做不作屢次,在beancopy的場景下使用CGLib代替BeanUtils,性能有超過20倍的提高;能夠提早作的工做提早作,IP庫二方包優化過程當中把不少冗餘操做提早處理掉,性能有接近1倍的提高。在技術選型時能夠針對場景選擇更優的二方包,好比LMAX-Exchange開源的高性能併發框架Disruptor。另外,若是改變了原來的二方包,代碼不能提交回主幹,未來會遇到版本升級困難的問題。
4.模板引擎優化
經過前面的分析可知,Velocity模板渲染是最大的模塊瓶頸,除了減少模板大小,還能夠從模板框架優化下手。由於Velocity是解釋型語言,性能相對較差;執行過程當中還有大量的反射調用,效率可想而知;字符字節的轉換也尤爲消耗CPU。淘寶基於Velocity開發了語法兼容的Sketch框架,將Velocity模板編譯成Java類執行,減小了反射調用,內部用字節存儲頁面,節約了從渲染到輸出的兩次編碼轉換。使用Sketch框架之後,不少應用總體吞吐量有超過20%的提高。另外,淘寶Sketch框架將於今年開源。
應用自身優化1.壓縮模板大小
在不少系統中,模板大小和吞吐量成反比,若是能大幅減少模板和HTML的大小,會給吞吐量帶來很大提高。最簡單的減少模板方法,就是刪除空行和多餘空格。對於URL比較多的頁面,去掉「http:」這五個可省略的字符、長URL壓縮、用URL別名代替全連接均可以帶來不錯的效果。若是for循環裏重複數據較多,能夠把數據移到for循環以外,屢次出現的只渲染一次,瀏覽器端渲染時再經過前端代碼把重複內容放回去,這種業務上的去重,每每能帶來很好的優化效果。
2.設置最佳併發
併發用戶數、資源利用率、吞吐量和響應時間的關係能夠參考下圖:
當服務器處於低負載區,隨着併發用戶數的增長,資源利用率和吞吐量直線上升,響應時間沒有明顯的變化;當服務器處於高負載區,隨着併發用戶數的增長,資源利用率趨於飽和,吞吐量達到最高點後開始降低,響應時間開始有明顯的增長;這時併發用戶數繼續增長,服務器則處於假死狀態,資源利用率繼續趨於飽和,吞吐量開始急劇降低,響應時間開始急劇上升,直到系統不能處理任何請求,咱們稱之爲服務器Down機。從這張圖裏咱們能夠得出,服務器吞吐量最大的時候對應的併發用戶數,就是這個服務器的最佳併發數,當併發用戶數大於這個值的時候,系統服務能力開始明顯降低,作優化要找到這個最佳併發數,經過穩定性模塊設置到系統中,穩定性模塊能夠對超過最佳併發數的請求進行限流,以保證系統達到最好的性能表現,不會由於大流量衝擊而垮掉。咱們通常經過線上壓測來肯定系統最佳併發數,對於CPU密集型應用也能夠用以下公式計算:
最佳併發=((CPU時間+CPU等待時間)/CPU時間)*CPU核數
3.代碼瓶頸優化
前面介紹瞭如何找到影響性能的瓶頸代碼,能夠針對代碼瓶頸進行優化。舉個例子,通過分析發現某系統每一個請求都拋異常吞異常,致使服務器資源利用率上不去,吞吐量不高,修正後CPU使用率提升30%,系統吞吐量也提高近30%。拋JDK默認的異常比較影響性能,尤爲是在線程調用棧很深的狀況下,有的系統還使用異常做業務流程控制,有的異常直接被吞掉,危害都很大。taobao-jdk開發了異常監測功能,從JVM層面直接發現和暴露全部異常問題,杜絕了這一類瓶頸的出現。
4.外部調用優化
淘寶系統目前處於第三代分佈式架構,爲了優化外部調用,開發了並行RPC、並行搜索等功能,對於適合的場景能夠有效下降響應時間。某些場景使用更優的ProtocolBuffers序列化框架,在某些對性能要求很高的場景,使用開發成本稍大、比ProtocolBuffers還快20%的Kryo框架。
5.面向CPU編程
對於CPU密集型應用,若是能減小CPU的使用則能夠直接提高系統吞吐量。針對Web應用能夠調低GZip壓縮級別來下降HTTPServer對CPU的消耗。針對核心代碼能夠面向CPU編程:常常一塊兒使用的Field能夠放在一塊兒,這樣對CPU緩存比較友好;在多核服務器上對性能要求比較高的場景,能夠補齊緩存行以減小僞共享的發生;按行處理不要按列處理數組,編寫符合空間局部性的代碼能夠很好地提高性能;使用源生批量接口處理數組,這樣一條CPU指令就能夠完成操做;使用樂觀策略(CAS)來代替同步和鎖也能夠有效提高性能。
6.架構優化
架構調整每每要對系統傷筋動骨,開發週期很長,但卻能夠帶來最好的優化效果。列舉幾個咱們經常使用的架構優化方法:動態資源靜態化,把須要服務器動態生成、更新不頻繁的內容CDN化,內容變化了能夠回源更新CDN,這樣大幅減小了服務器的動態內容輸出;後臺依賴前臺化,給後端服務暴露對外的HTTP接口,使服務器依賴轉變成JS依賴,能夠提高後端性能,而且把強依賴變成弱依賴,提高整個系統的穩定性;後端渲染前端化,對於數據遠小於面,頁面佈局比較規則的場景適用;DB依賴緩存化,這點業界用的很是之多;善用緩存,針對不一樣的場景能夠緩存對象、緩存頁面片斷、緩存整個頁面、緩存HTTP響應,使用緩存須要關注失效機制和數據預熱,並儘量提升緩存命中率。
前端性能優化
1.度量關鍵指標
咱們能夠經過前端埋點和NavigationTiming接口來採集網頁在用戶瀏覽器上的關鍵指標,包括DNS查詢時間、TCP鏈接創建時間、HTTP請求時間、頁面下載時間、開始渲染時間、domReady、可交互時間、onLoad時間,使用阿里度等工具能夠獲得首屏時間。有了這些指標就能夠衡量前端優化的效果。業界還有一些工具會給出不少優化建議,好比dynaTraceAJAXEdition、YSlow、Chrome插件SpeedTracer等,淘寶也根據Yahoo的34條軍規,開發了本身的TSlow。
2.前端性能優化
Yahoo34條軍規已經成爲前端WPO的標準,這裏再也不介紹。列幾個對咱們的場景比較適用的優化:減少Cookie大小;減小DNS查詢並適當增長不一樣域名以優化資源併發加載;針對瀏覽器渲染,減小Dom數、按需延遲加載、次要信息異步化加載、使用BigRender技術控制渲染節奏優化首屏時間、使用前端模板引擎進行頁面渲染。某應用後端數據大小是前端頁面大小的1/10,若是隻傳數據不傳頁面能夠大幅節省頁面下載時間,咱們採用前端渲染方案優化後,系統響應時間減小25%、頁面大小減少60%、domReady時間減小60%、onLoad時間減小70%。在前端性能優化領域,Google一直在引領潮流,基於Chrome這個入口推進不少優化落地、推出PageSpeed、WebP、SPDY等技術,帶給咱們不少新的方向。