京東三級列表頁持續架構優化—前端優化實踐

京東三級列表頁持續架構優化—前端優化實踐

王向維,京東商城三級列表頁架構師,完成列表頁的nodejs版本到nginx+lua版本的變遷,并做了大量三級列表頁的服務端和前端的優化工作。

在持續開發一個核心系統過程中,除了滿足業務需求外,還應該考慮系統未來的架構,追求極致的系統的可用性、高性能和穩定性。這個過程是一個長期積累和重構的過程。

每個應用都要滿足自己特定的需求,因為商業條件、應用場景、用戶期望,以及功能復雜性各不相同。盡管如此,如果應用必須對用戶作出響應,那我們就必須從用戶角度來考慮可感知的處理時間這個常量。事實上,雖然生活節奏越來越快——至少我們感覺如此,但人類的感知和反應時間則一直都沒有變過:

時間

感覺

0~100ms

很快

100~300ms

有一點點慢

300~1000ms

機械在工作呢

>1000ms

先干點別的吧

>10000ms

不能用了

這個表格解釋了Web 性能社區總結的經驗法則:必須250 ms 內渲染頁面,或者至少提供視覺反饋,才能保證用戶不走開。如果想讓人感覺很快,就必須在幾百 ms 內響應用戶操作。超過1s,用戶的預期流程就會中斷,心思就會向其他任務轉移,而超過10s,除非你有反饋,否則用戶基本上就會終止任務!

下面將從前端、服務器端、緩存、兜底等來說說如何優化京東三級列表頁。

前端優化

京東三級列表頁從優化到上線,已經經歷了兩個618和一個雙11的考驗,每天有上億的訪問量,頁面打開時間在20-80毫秒(在某些地區或低帶寬下會大于100ms)。

優化四原則

精簡和瘦身頁面,首屏優先展示出來;

需用戶交互的部分惰性加載;

能不執行的先別執行,惰性執行;

滾屏惰性加載。

一、HTML文檔要精簡

目的:盡快渲染出頁面并達到可交互的狀態。

方法:

1、如果非必須,盡量只生成首屏需要的html數據;

2、優先獲取資源、提前解析。如首屏需要的css和js;如果不考慮維護成本,可以把首屏需要的css和js放到文檔中;

3、發現和優先安排關鍵網絡資源,盡早分派請求并取得頁面;

4、文檔精簡后,服務端生成程序耗時短,性能才會好。

如列表頁的頭、面包屑、品牌區、屬性篩選區、60個商品主圖數據,這些是服務端模板渲染輸出;而剩余部分是在前端JS惰性加載或生成。

二、需用戶交互的部分惰性加載

對于三級列表頁品牌區,服務端只渲染18個品牌,用戶在點更多時,ajax異步加載其他的。對于整個屬性是篩選區服務端只渲染5行,其他行用戶在點更多時,js從文檔嵌入資源中取到數據,并渲染成html。這樣做可以保證服務端計算少,提升服務端性能,減少數據傳輸。如下圖點“更多”時才加載更多的品牌,因為有些三級類目有非常多品牌,如果不采用這種方式,整個頁面渲染非常慢。

因為需要SEO的原因,京東三級列表頁不能使用bigpipe等技術來進行更優的處理。

三、能不執行的先別執行,惰性執行

上圖是三級列表頁最重要的商品區(商品主圖+N個關聯商品小圖),每個商品的區域都是完全一樣的;如果在服務端拼裝整個商品區域的話,尤其涉及到小圖部分,會有非常多的重復html元素;我們把體驗和減少頁面內容進行了折中處理;服務端渲染輸出商品主圖部分;小圖部分通過json數據嵌入到頁面,然后通過js惰性執行渲染。這樣可以很好地對頁面進行瘦身。而且小圖資源是頁面嵌入的,非異步加載;沒有網絡請求,用戶基本感知不到異步帶來的渲染閃動問題。下圖就是頁面嵌入的小圖json數據。

四、滾屏惰性加載

三級列表頁的60個商品區域的圖片和頁尾都是當用戶向下滾動頁面時,才去加載當前屏幕中的圖片和模塊。這樣可以節省服務器帶寬和壓力,提升頁面整體渲染時間。

上邊就介紹完了三級列表頁在優化時使用的最主要的四個原則,而實際優化過程中,還涉及到非常多的優化細節,如下部分將介紹這些細節。

將一些JS/CSS資源直接嵌入頁面

把資源嵌入文檔可以減少請求的次數。比如頁面需要的js 、css數據。如下圖所示:

上圖中的這些js對象,是后端渲染輸出的,因此不適合放入單獨的js文件,直接在頁面中嵌入輸出會更好些。slaveWareList是小圖的列表對象。如果放在服務端模板渲染輸出的話,首先需要進行一些循環拼裝頁面;另外會使頁面體積變得非常大。權衡之后決定放到前端js渲染輸出。這樣也帶來了一些好處:減輕服務端壓力,提升渲染模板性能和減少服務端執行時間;服務端不用生成html,文檔減少上百個div,減少頁面大小和網絡開銷;提前放到文檔中,不用異步調用;用戶基本感知不到渲染過程。

對引入的資源排定優先次序

根據自己系統的業務,對每種資源定優先級:對必需的資源優先加載,而低優先級的請求保存在隊列中延時加載或等待必需資源加載完再加載;如:搜索推薦熱詞、頂部三個熱賣商品接口、60個主商品的圖片、價格優先加載。而對于庫存、促銷信息、廣告詞、預售商品、店鋪信息等,延后加載。對于點擊流,廣告統計數據則延時兩秒再加載。

應用js緩存來存儲公有屬性和商品信息屬性

三級列表頁中的每個商品都是一個對象,存放在一個map中,通過ajax接口異步填充和維護商品的屬性。用于后續用戶交互用。同時維護成本也會降低;即頁面中用到的每個商品數據放入一個map中,如果沒有則異步加載;如果有直接使用;即這些數據是公共數據。

Ajax接口最優調用

頁面往往依賴很多的異步接口,因此要對異步接口進行壓測,找出接口的最優調用方式。如京東三級列表頁依賴價格、庫存、廣告詞、店鋪信息等異步調用接口。而頁面有時候會出現多達300多個商品,如果用一個get請求把這些sku做參數,性能非常慢,那么就要采用分組分批調用。如頁面商品在300個時,價格接口分六組,第一組30個,第二組30個,第三組60個,第四組60個,第五組100個,第六組100個。

DNS預解析

對可能的域名進行提前解析,避免將來HTTP請求時的DNS延遲。如對價格、庫存、圖片、單品頁等服務預解析。

減少HTTP重定向

HTTP 重定向極費時間,特別是不同域名之間的重定向,更加費時;這里面既有額外的DNS 查詢、TCP 握手,還有其他延遲。最佳的重定向次數為零。比如三級列表頁以前是http://list.jd.com/12-12-12.html,而現在是http://list.jd.com/list.html?cat=12,12,12;在過渡期間可以重定向,但是過渡完成后就沒必要重定向了。

使用CDN(內容分發網絡)

把數據放到離用戶地理位置更近的地方,可以顯著減少每次TCP連接的網絡延遲,增大吞吐量。比如京東三級列表頁、商品詳情頁、公共JS、CSS。

傳輸壓縮過的內容(Gzip壓縮)

傳輸前應該壓縮應用資源,把要傳輸的字節減至最少:確保對每種要傳輸的資源采用最好的壓縮手段。所有文本資源都應該使用Gzip壓縮,然后再在客戶端與服務端間傳輸。一般來說,Gzip可以減少60%~80%的文件大小,也是一個相對簡單(只要在服務器上配置一個選項),但優化效果較好的舉措。(對于壓縮級別,經過不同服務器多次壓測,建議Nginx設置為1-4)

去掉不必要的資源

任何請求都不如沒有請求快,把一些非必須的或者可異步的,或者可延遲的盡量延遲請求。

在客戶端緩存資源

應該緩存應用資源,從而避免每次請求都發送相同的內容。

無狀態域名

Cookie 在很多應用中都是常見的性能瓶頸,很多開發者都會忽略它給每次請求增加的額外負擔;減少請求的HTTP首部數據(比如HTTP cookie),節省的時間相當于幾次往返的延遲時間。如列表頁依賴的價格、庫存接口,采用3.cn無狀態域名,從而減少主域下cookie傳輸。

并行處理請求和響應

請求和響應的排隊都會導致延遲,無論是客戶端還是服務器端。這一點經常被忽視,但卻會無謂地導致很長延遲。

域名分區

當頁面中非常多請求都是一個域名下資源時,由于瀏覽器同時只能打開6個連接池,而且每個鏈接池是對不同域名起作用,所以很多請求一個域名會出現排隊現象。如果把這些請求域名分區,讓請求并行,從而加快資源下載。如:頁面需要下載上百張圖片,對圖片進行域名分區調用。京東大部分頁面都對圖片進行了域名分區調用:

http://img10.360buyimg.com/

http://img11.360buyimg.com/

http://img12.360buyimg.com/

http://img13.360buyimg.com/

http://img14.360buyimg.com/

拼合和連接

合并鏈接:把多個JavaScript 或CSS 文件組合為一個文件。拼合:把多張圖片組合為一個更大的復合的圖片(CSS?Sprites)。

服務端寫相關信息到header

把服務器IP后兩位寫到header,如果有問題,方便定位哪臺服務器。ups:后端路由的所有服務器都取到。把緩存命中信息或異常走兜底了,把后端運行狀態寫到header。Head-status:命中、未命中、異常等狀態。

服務端架構

Nginx+Lua(OpenResty)+golang+redis緩存計算,后續再把列表頁的架構整理出來。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,637評論 25 708
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 此文是開濤在【三體高可用架構群】之分享內容,“三體”是為了紀念三體一書對技術人的偉大影響而冠名。 張開濤:2014...
    安東的漫長歲月閱讀 2,425評論 0 14
  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標簽默認的外補...
    _Yfling閱讀 13,776評論 1 92
  • 深夜思母恩,悔之已晚以。 還未盡孝道,只嘆離世早。 一念母親好,耐心哺乳小。 二念母親好,歸家有人管。 三念母親好...
    微微的微笑66閱讀 629評論 0 1