技術|京東前臺PC首頁系統技術詳解


前言

京東零售系統在2018年實現前中臺架構的調整與劃分。中臺實現基礎服務組件開發,前臺主要對接用戶請求,通過對中臺RPC數據的聚合,來滿足用戶多樣化需求。其前臺系統又分為首頁系統、單品系統、搜索系統、列表系統、訂單系統等等。

作為PC首頁研發,本文主要講解PC首頁的技術實現。

PC首頁業務邏輯從最初的模板渲染,到後來的SSI模塊加載以及現在的前後端分離。開發語言也從之前的ASP、PHP逐步過渡到以LUA為主的技術框架。通過技術迭代升級,首頁頁面打開速度從之前的200ms縮減到30ms內,API性能從之前的500ms優化至100ms左右。

技術實踐

京東零售系統,每天承載著億萬網民的購物需求。PC首頁又是京東商城的一級入口,所以系統必須達到以下3方面要求:頁面完整性(容災、兜底、降級);流暢的加載速度(高併發、頁面加載優化、API加載優化);監控&告警。

下面將根據系統設定目標逐步講解首頁系統實現方案。

1.頁面完整性

頁面完整性指任何時候訪問頁面均需呈現正常的頁面樣式,不得出現天窗以及非200狀態碼(40x、50x等)。如下圖樓層,若將單模塊缺失直接呈現給用戶,將導致頁面天窗,影響體驗。在單模塊異常時,系統可對整樓層隱藏操作,優先保證頁面完整顯示。

技術|京東前臺PC首頁系統技術詳解


下面從6方面講解容災降級的業務邏輯:

頁面的容災:前端頁面主要承載頁面骨架,也會包含部分開關配置及必要的兜底數據,即使後端API服務異常,html依然可以提供基礎的用戶體驗。前端頁面由模板渲染生成,其中模板變量通過配置中心(蜂巢系統)維護,定時worker觸發生成前端頁面,然後推送到靜態資源服務器,最終通過CDN加速提供服務。其中在worker觸發生成html階段執行多層驗證邏輯,保證html的完整性。

接口的容災:一般接口邏輯均是優先讀取緩存數據,若緩存失效則繼續讀取上游RPC數據來輸出。但上游系統均不保證100%可用,如何保證在上游異常的情況下提供可靠服務使頁面正常渲染呢?PC首頁系統在接口層面做了2方面工作:

第一、接口在讀取&聚合上游RPC數據後會保存兩部分緩存,一個為正常用戶加速緩存,一般會設置5min過期時間,一個是兜底緩存,永不過期。當緩存或者上游RPC服務均不可用時,接口會讀取兜底緩存保證正常的數據輸出。

第二、如果API服務由於自身問題(例如宿主機異常、Redis服務異常、用戶網絡抖動等)導致無法提供正常的服務怎麼辦。為了避免這個問題,系統為API提供了一條新的訪問途徑-CDN API。CDN API會訪問由Worker定時生成的靜態結果集(File)輸出數據。當前端異步加載數據感知常規線路異常後自動觸發CDN API線路來保證接口可用性。

依據上面2種兜底邏輯,繪製如下流程圖,此邏輯將首頁API服務的可靠率提高至100%。

技術|京東前臺PC首頁系統技術詳解


接口的降級:如下圖所示,正常情況接口通過讀取Cache、RPC數據、兜底Cache提供服務。但如果Cache和RPC服務均異常,接口的響應時間就大大浪費在前兩階段(10ms+100ms)。為了避免此情況的發生,系統通過配置中心(蜂巢系統)設置降級開關,當系統感知降級開關打開時,將直接讀取兜底Cache,保證API的響應速度。

技術|京東前臺PC首頁系統技術詳解


NGINX的容災:NGINX容災指系統監控到接口非200狀態碼的時候在NGINX層面執行兜底邏輯,此兜底邏輯可以讀取(或代理)遠程靜態資源實現透明容災。具體實現通過error_page指令完成,error_page指令可以在特定的狀態碼設置一個named location,並在其代碼塊中執行相應的兜底業務邏輯。樣例配置如下:


error_page 301 302 404 500 502 504 = @error_callback;

location @error_ callback {

#實現業務邏輯

proxy_pass http://remote_server;

}

樓層容災:在多模塊樓層,前端會調用多個API進行頁面渲染。如下圖所示,此樓層一共包含4個模塊,每個模塊均提供獨立的API接口。當其中一個模塊API異常,即觸發樓層隱藏動作,避免天窗的出現。(這裡執行的是頁面可以有損但必須完整的策略)

技術|京東前臺PC首頁系統技術詳解


終極容災(頁面CDN兜底):在2016年,啟動“永不消失的首頁”項目,即不論情況如何極端(包括Redis服務異常、服務器異常、RPC異常、網絡異常等等),系統均可快速響應並切換至歷史頁面來保證頁面完整。

為實現歷史快照,項目借鑑爬蟲技術,定時抓取PC首頁的完整數據,並將快照數據推送至靜態服務器。當監控到系統異常時開啟降級開關將頁面及時回滾至特定歷史版本。此降級繞開正常的服務流程,直接讀取兜底的靜態資源。具體流程如下:

技術|京東前臺PC首頁系統技術詳解


(此項目自上線後尚未觸發一次。在其他的業務場景中,需要考慮各自實際情況)

2.流暢的加載速度

既要保證頁面完整性,也要保證頁面以及API的加載速度。下面從性能方面講解系統的優化方向。

技術選型

首頁是一種重性能、輕邏輯的業務場景,沒有過多的基礎服務依賴,主要與Redis和上游RPC做交互。系統通過讀取上游RPC數據後聚合、過濾、驗證後輸出,最終完成頁面展示。用戶請求簡化流程圖如下:

技術|京東前臺PC首頁系統技術詳解


結合業務場景,京東首頁在2015年開始調研並嘗試使用OpenResty服務,基於NGINX的異步事件模型以及高性能的腳本語言LUA,OpenResty完美勝任京東首頁的高併發場景。經過近幾年的沉澱,OpenResty成為京東首頁的基礎架構,也以此沉澱了OpenLua開發框架。

技術|京東前臺PC首頁系統技術詳解


如上圖所示,OpenResty(Nginx)服務的請求處理擁有11個階段之多,每個階段都有其特定的業務場景。在init_by_lua*階段,框架初始化環境變量、init_worker_by_lua*階段初始化降級開關以及基礎配置,並將其同步至共享cache(ngx.shared.DICT)、 init_write_by_lua*階段初始化路由策略、access_by_lua*階段實現訪問權限驗證,加解密等、content_by_lua*階段實現主體業務邏輯、log_by_lua*階段實現日誌以及Ump信息的輸出。

Redis HotKey(熱key)優化:在多數的業務場景中都使用Redis集群為服務加速,通過集群的橫向擴展能力增加系統吞吐率。但在特殊的業務場景會有熱點數據的出現,導致流量傾斜,增加單個分片的壓力,這樣就極大制約API的效率,極端情況甚至可以拖垮Redis集群。如何避免熱點數據有2種方案:1、通過複製熱點數據將數據存放到不同的Redis分片,每次通過隨機算法讀取;2、熱點數據前置、減少Redis的壓力。由於第一種方案實現繁瑣,系統使用第二種方案解決熱點數據,也就是將發現的熱點數據存儲到本地cache中(ngx.shard.DICT),通過本地cache服務直接對外提供服務,由於ngx.shard.DICT效率極高,極大優化API的響應時間。

Redis BigKey優化:項目開發中BigKey是不可忽略的性能點,可能在前期開發以及壓測時均可以完美達到設定值,但是線上環境效率不理想,這時候就要考慮bigkey的因素了。由於bigkey佔用內存較多,不但會使網卡成為瓶頸,在set、get也會阻塞其他操作,直接導致Redis吞吐量下降。

在PC首頁系統bigkey的定義範圍為>5k,只要key的值大於5k均需要單獨處理。處理方案依然是2種。1、業務上做切割。例如一個業務場景需要將一個大的商品列表放到Cache中, 一般做法是通過kv(set key value)保存到Redis中。這時業務優化方案是將一個key擴展到1+n個key,一個key存儲id list,其餘n個key存儲id對應的詳情信息,這樣就將bigkey打散為多個key。2、技術上做切割。例如系統定義的bigkey為5k,通過算法將value以5k的界限做切割,然後系統存儲一個關係key以及n個小key實現bigkey轉小key的過程。

不管使用哪個方案,目標是一致的,避免bigkey的出現。在將bigkey轉小key的過程中,合理使用管道技術減少redis的網路消耗。

分佈式任務的使用:隨著個性化推薦算法的推廣,商城首頁全面接入推薦算法。由於個性化算法基於實時計算,耗時較高,為了保證用戶瀏覽的流暢度,系統引入分佈式服務。如下圖所示,用戶在請求第一屏的時候將用戶信息push到分佈式任務系統,分佈式任務利用時間差計算下面樓層的個性化數據並存儲到Redis。當用戶訪問到特定樓層即可快速加載數據。

技術|京東前臺PC首頁系統技術詳解


併發請求:每個API都會涉及多個上游RPC服務,為了避免串行請求帶來的耗時累加,系統將每個上游RPC封裝成單個子API服務,然後通過ngx.location.capture_multi命令實現併發請求來優化API耗時。

磁盤IO優化:為了減少磁盤io,系統日誌模塊使用批量寫入策略來減少磁盤io的消耗。在OpenResty中每個請求(包含自請求)都會初始化一個 ngx.ctx全局表,首頁應用將當前請求在不同階段產生的日誌內容統一寫入ngx.ctx.Log表中,並在log_by_lua*階段統一觸發io操作將日誌寫入文件系統。

圖片優化:電商系統頁面會引入大量的圖片,雖然圖片系統引入CDN服務,但單個域在高併發的時候依然會有堵塞。為了解決單域問題,我們將圖片部署到多個域上,例如img10.360buyimg.com、img11.360buyimg.com、img12.360buyimg.com等等。但多域會疊加DNS解析耗時,此時頁面可以引入預解析指令(dns-prefetch)來減少dns的解析時間,提高圖片加載速度。另外使用懶加載也是優化的一個技巧。

3.監控&報警

原有的報警信息通過 “系統日誌->研發->產品->運營”的方式層層傳遞,不但處理問題時效長,而且佔用研發資源。為了簡化報警流程,現有系統直接將報警信息同步給產品&運營,減少信息流通環節,提高效率。

增加報警邏輯的同時不影響系統性能,系統通過異步非阻塞的ngx.socket.tcp組件實現消息同步。具體流程為系統將日誌通過ngx.socket.tcp組件傳遞給分佈式任務,分佈式任務通過異步方式將日誌同步到Elastic集群。報警系統根據報警規則觸發報警信息發送。(注:使用ngx.socket.tcp組件請合理使用連接池,避免頻繁建立連接)

小結

隨著首頁系統的迭代升級,團隊沉澱了蜂巢系統平臺以及OpenLua框架。蜂巢系統通過積木賦能、組件化思想搭建,可快速響應業務需求。OpenLua框架實現Lua的MVC分層結構,並融入京東內部組件,在滿足API性能的同時提升開發效率。

繼續前行,永不止步。在之後的的研發道路上,將繼續沉澱業務需求,強化組件化、標準化思想,深耕蜂巢系統、OpenLua框架,使之更加靈活、高效、易用。

技術|京東前臺PC首頁系統技術詳解



分享到:


相關文章: