天天看點

京東購物車如何提升30%性能

1、背景

京東購物車如何提升30%性能



購物車面臨的挑戰:

1)新業務:随着業務形态的豐富,購物車在不斷支援各種新業務,依賴的外部接口也随之增加;

2)下沉:一些前端調用的接口下沉到購物車中台;

3)前置:結算流程很多業務前置到購物車中,如優惠券、京豆;

4)擴容:為改善使用者體驗購物車可容納的商品數量在不斷增長;

這些導緻購物車依賴的RPC接口數量及分頁調用次數都在不斷增加。購物車作為交易流程開端,本身流量較大,在業務複雜化的背景下,如何提高性能保證使用者體驗,成為購物車面臨的較大挑戰。

2、全異步化改造方案

通過增加伺服器資源雖然能在一定程度上解決問題,但會帶來較大的成本開銷,也與工匠精神相悖。能否通過技術手段提升性能呢?通過分析,異步化改造成為解決這一問題的有效手段。

1)不同RPC并行

購物車依賴接口達幾十個,各接口間存在複雜依賴關系。必須先梳理各接口間依賴,識别哪些可以并行。然後将原有代碼拆分為兩部分:RPC異步請求和結果處理,按照依賴關系,讓RPC最大限度并行執行,減少在結果處理階段異步響應等待時間,進而達到提升性能的目的。

京東購物車如何提升30%性能

2)批量接口多分頁并行

購物車依賴接口多為批量接口,且單次調用有資料量限制,需将資料拆分為多個分頁調用。那麼多個分頁間也可以并行,改造中封裝了異步分頁工具,使業務層對分頁邏輯無感覺,異步工具自動将超過接口上限的資料拆分為多個分頁并行調用,提升單接口響應速度。

3)底層采用JSF異步調用

異步調用基于京東RPC架構JSF,推薦使用1.7.5以後版本,支援CompletableFuture。

3、問題及解決

異步化改造的總體方案并不複雜,但是在實際落地過程中,遇到了很多細節問題:

1)異常重試需精細化

同步調用時,如果逾時會重新調用。改為異步後重試會失效,因為在調用時一般不會報錯,需要在結果處理階段擷取異步響應逾時後,再進行重試。

另外,多分頁并行時,當某一頁請求逾時後,應該隻重試出錯的分頁。底層對分頁調用進行了封裝,上層業務代碼在擷取資料時無法感覺是哪一頁逾時,是以必須在異步調用時将現場資訊儲存在包裝類中,一起傳回給業務層,在Get資料逾時後,單獨重試出錯的分頁。

發生異常時,并不是所有情況都需要重試,當遇到限流等異常時,不能進行重試。底層工具需要自動過濾限流異常,當然也支援自定義規則。

2)異步RPC監控更複雜

底層RPC耗時監控需要拆分為兩部分,在分頁調用時記為開始時間,在異步結果到達後,記為結束時間。如果調用異常或Get逾時,需要标記本次調用失敗。對于重試同樣需要記錄調用耗時,且正常調用與重試調用需分開記錄。

除了需要監控RPC耗時外,還需要監控結果處理階段Get等待時長,這個時間才是真正對應用性能有影響的時間。由于底層是分頁調用,是以業務調用次數和底層RPC調用次數并不相同。

3)分頁異步結果不能合并,否則無法擷取異常Provider資訊

底層異步調用結果,必須通過包裝類原樣傳回給上層,除了上邊提到的需要單分頁重試外,另一個原因是必須保留異步結果,在分頁逾時後才能輸出逾時的Provider資訊。這是由于Provider資訊依賴JSF架構的JSFCompletableFuture,如果在底層合并結果,會導緻資訊丢失。

4)每頁逾時時間需單獨控制

京東購物車如何提升30%性能



分頁調用過程如上圖所示,在結果處理時,每頁Get逾時時間需要單獨控制,因為擷取結果是順序進行,擷取後邊的分頁時,前邊分頁等待的時間也應計算在内,以保證整個擷取結果的時間不超過單個分頁的最大逾時時間。計算公式如下:

逾時=RPC逾時時間 > (目前時間-異步調用開始時間) ? RPC逾時時間 – (目前時間-異步調用開始時間) : 0

5)分頁均衡

為避免最後一頁資料過少造成資料傾斜,需要将請求資料均分到每一頁,以最大限度提高整個請求的性能。

4、收益

改造完成後購物車核心接口耗時減少30%,保證使用者體驗,節省大量伺服器資源。後續增加新的RPC接口時,隻要處在調用拓撲的非關鍵路徑上,對購物車性能沒有太大影響。另外,容量增加時除少數不能分頁調用的接口外,對性能影響已經比較小。

京東購物車如何提升30%性能

作者:京東零售 王利輝

内容來源:京東雲開發者社群