1. 程式人生 > >京東三級列表頁持續架構優化—Golang+Lua(OpenResty)最佳實踐

京東三級列表頁持續架構優化—Golang+Lua(OpenResty)最佳實踐

作者:謝剛,京東商城架構師,負責京東分類列表、鳳凰等系統的架構開發工作;之前在搜狐視訊負責UGC視訊架構開發工作。

***招聘:京東列表頁目前有Java、Golang、Lua(OpenResty)、資料探勘等職位,歡迎投簡歷到[email protected],期待與您的合作***

承接上篇《》。

分類列表入口

分類列表入口,可以通過京東首頁首屏左側導航進入,是使用者購買商品的幾大入口之一。

分類列表,展示各個分類的商品,有綜合排序、價格排序、銷量排序、上架時間排序、圖書還有出版時間排序。可以按照品牌、價格和各種擴充套件屬性篩選出想要的商品。下圖以空調列表為例。

分類列表特點

  • 分類多,全站大概幾千個分類;

  • 商品多,每個分類商品多,有的分類達上千萬的商品;

  • 需求多樣化,不同分類需求不一樣,例如大家電、圖書需求各不一樣;

  • 請求量大,實時性要求高。

舊架構

舊架構,前端是用nodejs做模板渲染,後端服務是調用搜索介面。舊架構缺點:

  • 響應時間比較長;

  • 因為是搜尋返回的資料,資料二次加工不方便。

升級新架構 * 新架構設計目標

  • 分散式,資料可以做多個分片,服務各層可以做到水平擴容;

  • 高可用,雙機房雙活部署;

  • 響應迅速;

  • 資料閉環,線上服務主要資料不依賴於外部API;

  • 運維便捷,方便切換叢集,方便分類管理配置;

  • 資料提升,通過優化排序演算法,提升GMV、訂單轉化率、客單價等。

* 新架構

新架構功能模組如上圖所示:

  • 頁面渲染:採用OpenResty(Nginx+Lua)來作模板渲染,方便頁面邏輯的調整;

  • 業務處理:採用golang,所有的篩選、過濾邏輯都是在這一層處理的;

  • 資料異構:頁面渲染需要相關的資料、過濾篩選需要的資料,都是通過異構過來的;

  • 訊息處理:通過接入MQ訊息,可以實時處理商品上下架、庫存更新、價格修改等訊息;

  • 質量分計算:通過大資料平臺計算商品質量分,為綜合排序提供依據;

  • 配置管理中心:負責後臺排程、分類配置等。

新架構線上流程如下圖所示

新架構離線資料流如下圖所示

其中:

  • 資料集市,使用的是京東的大資料平臺;

  • JSS,是京東自研分散式檔案儲存系統;

  • JIMDB,是京東自研KV儲存系統,可當分散式Redis使用。

詳解各個模組 * 質量分計算

由於每個分類的商品非常多,個別分類達千萬量級的SKU,而使用者瀏覽的SKU有限,我們需要將使用者最可能買商品排在前面;為每個分類的所有sku進行質量分計算,涉及到幾十個指標(包括銷量、評價、瀏覽、轉化率等);根據質量分的高低進行排序;由於涉及資料量很大,所有計算都在大資料平臺完成;將計算結果推送到JSS。

由於還有一些特殊規則,例如品牌穿插、店鋪穿插、特殊排序等,這些規則的實現是通過worker實現,讀取jss,並進行特殊規則處理。將處理後的資料推送到MYSQL。

* 異構服務

異構服務主要是異構過濾和展示需要的商品資料;呼叫外部各個介面,形成一張商品寬表。如下圖所示:

* 業務處理子系統

上圖展示了列表各種篩選邏輯,排序邏輯。

業務處理子系統提供前端所需要的所有過濾篩選介面,以及展示資料。該系統採用golang開發,所有篩選資料都存在記憶體中,提高檢索速度;展示的資料都放在jimdb中,目的減少佔用記憶體大小,縮短golang的GC時間。下圖展示了記憶體中儲存的資料。

* 訊息處理系統

該系統接收處理相關訊息(商品變更,上下架,價格變更,庫存變更),並實時更新到線上,如下圖所示:

* 頁面展示子系統

頁面展示子系統,採用Nginx+Lua實現,負責模板的渲染,如下圖所示。

為了提高頁面的渲染速度,有一部分頁面採用非同步渲染,例如:頁面小圖聚合的可以讓js渲染小圖;超過5個的擴充套件屬性,讓js非同步渲染。頁面需要的價格資料、庫存資料、廣告資料,採用非同步載入;保證這些資料的實時性。

頁面渲染優化:

  • HTML文件精簡,越簡單渲染越快,效能越好;例如:頁面小圖聚合的可以讓js渲染小圖;超過5個的擴充套件屬性,讓js 非同步渲染;

  • 懶載入資料,例如:滾屏載入圖片和頁尾;

  • 資源載入排序,對每種資源定優先順序,對必需的資源優先載入,而低優先順序的請求儲存在佇列中延時載入或等必需資源載入完再載入。例如:搜尋推薦熱詞、頂部三個熱賣商品介面、價格優先載入。對於庫存、促銷資訊、廣告詞、預售商品、店鋪資訊等,延後載入; 對於點選流,廣告統計資料則延時兩秒再載入;

  • 頁面更多優化參考:《》。

Golang+Lua(OpenResty)的應用

* Golang–遇到的坑

  • JSON的序列化效能低下:Golang內建的encoding/json、encoding/gob,採用ffjson;

  • GC問題:減少記憶體物件。減少物件申請,兩個作用:減少記憶體使用,減少記憶體碎片;

  • 字串拼接:儘量使用byte陣列,不要用String,由於String會建立新物件;

  • Go佔用OS記憶體釋放慢:執行:debug.freeOSMemory();

  • Goroutine閃退:goroutine閃退,導致應用程序閃退,異常捕獲;

  • 併發處理map:必須加讀寫鎖(sync.RWMutex)。

* 選擇Lua(OpenResty)

  • Lua:輕量級、協程、嵌入式、開發效率高;

  • OpenResty:OpenResty將Nginx核心、LuaJIT、許多有用的Lua庫和Nginx第三方模組打包在一起的web應用開發框架。

** 模板渲染

使用的模板引擎https://github.com/bungle/lua-resty-template。Nginx配置如下所示。

模板如下所示。

** 快取

快取:

  • 為後端服務異常提供託底資料;

  • 當流量太大時,可以開啟快取,減少後端服務壓力。

快取流程:

  • 解析url,對url做hash,得到相應的key,從後端服務獲取資料,如果資料完整,則渲染模版,將對應的資料放入對應的快取,並將key放入keycache,並設定快取時間;

  • 頁面快取是永不過期的,當key過期時,主動替換掉;

  • 為什麼分為兩類快取:firstpage cache只快取每個分類首頁的資料,這樣可以快取全部分類的首頁,保證所有分類都有託底資料。Otherpage cache 快取除首頁以外頁面,這樣保證熱點資料都在快取中。如果超過容量,通過lru淘汰;

  • 為什麼每類快取多個分片:因為lua_shared_dict存在自旋鎖,單片讀寫壓力大時,會有一定的瓶頸,因此採用多個分片,每個分片大小設定,根據具體快取資料來定;

  • Firstpage和otherpage 是快取在每臺nginx伺服器上,快取的內容有限;

  • Redis快取,可以集中快取,能夠快取更多的資料;

** 異常處理

異常處理分為兩層託底,保證每層報錯,均可對異常進行處理,無5xx等錯誤,提高使用者體驗,第一層託底,展示各個分類首頁的快取;第二層託底,跳轉京東首頁。

Lua執行問題,通過nginx配置error_page,進入異常處理。介面響應問題,通過ngx.exec內部跳轉,進入異常處理。

注:error_page預設只匹配一次,匹配多次需配置recursive_error_pageson;

ngx.exec為內部跳轉,類似於流水線,資料流動方向單一,無額外http請求。

新版效能* 頁面渲染效能

頁面響應時間:模板渲染+業務篩選介面(go),平均在30ms左右,tp99在80ms以內,提高6倍以上;頁面渲染(NGINX+LUA)TPS,在併發100時,16核單機在3500筆/秒,提高10倍左右。

* 業務篩選介面(GO)效能

業務篩選介面(GO):平均在10ms以內,tp99在50ms左右,響應時間提高6倍以上。

=======京東網站招聘=======

京東列表頁目前有Java、Golang、Lua(OpenResty)、資料探勘等職位,歡迎投簡歷到[email protected],期待與您的合作~

======個人公眾號推薦======

春天的旁邊,springside作者,唯品會一線老司機,by 江南白衣。

=======活動友情推薦=======

12月9-12日,由msup主辦的第五屆TOP100summit活動要開始了,將有100個軟體研發設計的案例在會上分享,歡迎有興趣的朋友參加!

點選「閱讀原文」進入報名頁面選擇「體驗票」提交相關資訊,並輸入專屬優惠碼 kaitao,就有機會免費獲得大會2天(12月9-10號)的體驗票。快來參與吧!