京東購物車分頁方案探索和落地
隨著京東購物車應用場景的豐富化和加車渠道的多元化,京東購物車的商品容量從2015年至今一直在逐步增加。
- 2015年京東購物車由80件擴容到120件;
- 2018年由120件擴容到150件;
- 2020年由150件擴容到180件;
- 2021年京東PLUS會員擴容到了220件。
持續不斷的擴容給我們的后端服務帶來了巨大的負載壓力,因為用戶購物車中商品種類數量的增加對應到后端的計算資源也會線性增加,如何做到資源最大限度的節約又能保證業務和用戶的體驗不受影響,如何從技術和業務層面綜合考量為逐步擴容的購物車訴求做好底層的支撐,一直以來都是擺在我們面前的一個痛點和挑戰。
首先描述下京東購物車的特性:
用戶在購物車操作完商品后會記錄下用戶當前的操作狀態,比如勾選,反勾選,切換促銷后的商品促銷信息等,當用戶再次進入購物車后會全量獲取購物車中的所有商品,根據商品的勾選態,促銷等實時計算商品的價格,展示給用戶。每次刷新或者修改購物車商品都是全量數據下發。持續擴容勢必會持續加大后端服務的壓力,同時購物車頁面的布局計算、渲染等操作不僅使用戶等待頁面刷新的時間變長,而且還會占用大量的內存資源,導致手機卡頓。
京東購物車為了提升用戶體驗,保留了勾選商品總額、優惠促銷、運費等一系列整車維度的計算邏輯,最終導致目前無法一步到位去實現購物車主商品的分頁。期間也對行業內的主流電商類APP做了充分的調研,大部分APP都沒有做購物車分頁且購物車容量上限也大都控制在120以下,做了分頁的APP也在勾選態保留和全局優惠計算等方面做了一些簡化和降級,所以我們決定從另一個方向進行探索和突破,即商品附屬信息分頁,暫時避開會影響到全局優惠計算和影響業務玩法、流量轉化的主數據分頁。
什么是商品基礎信息和附屬信息?
商品基礎信息和商品附屬信息的劃分主要從上游接口層面進行區分,商品基礎信息即從購物車中臺直接獲取的商品信息,比如商品圖片、商品名稱、商品價格、商品類型等;基于基礎信息,通過異步并行框架分批獲取的商品的附屬信息,比如優惠券、預估到手價、商品庫存、活動標簽、服務、秒殺、閃購等。
圖1 商品信息示例
02 目標
理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板后進行模板加載,加載階段會將產物轉換為視圖樹的結構,轉換完成后將通過表達式引擎解析表達式并取得正確的值,通過事件解析引擎解析用戶自定義事件并完成事件的綁定,完成解析賦值以及事件綁定后進行視圖的渲染,最終將目標頁面展示到屏幕
-
提升用戶體驗,解決由于上游服務接口無法支撐購物車超多商品并發訪問而導致的產品體驗問題,在無損用戶體驗的情況下,保證用戶在購物車滑動過程中無感知分頁加載商品附屬信息;
-
縮減機器成本,減少不必要的上游接口請求,降低后端服務器負載;
03 技術方案
理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板后進行模板加載,加載階段會將產物轉換為視圖樹的結構,轉換完成后將通過表達式引擎解析表達式并取得正確的值,通過事件解析引擎解析用戶自定義事件并完成事件的綁定,完成解析賦值以及事件綁定后進行視圖的渲染,最終將目標頁面展示到屏幕。從設計稿出發,提升頁面搭建效率,亟需解決的核心問題有:
1)商品附屬信息分頁加載價值分析
根據購物車線上不同維度埋點數據分析結果顯示,京東購物車中商品數量在20-220區間的請求次數占總請求次數一半以上,平均一屏展示的商品數量不超過3個,購物車中商品瀏覽的平均曝光深度6~7個,由此分析大部分的上游接口調用都有很大節省空間。通過前端線上模擬分頁埋點分析預估,商品附屬信息分頁調用的方式可以減少30+%的上游異步接口調用,做到在無損用戶體驗的情況下,削減接口調用峰值,降低接口的性能壓力和機器資源消耗。
2)商品附屬信息分頁加載
商品附屬信息分頁前后接口交互的差異在下圖進行了清晰的標識,主要體現在頁面刷新和頁面滑動兩個方面。
圖2 異步請求分頁方案
商品附屬信息不分頁加載方案: 客戶端觸發一次刷新操作需要從各個上游接口獲取所有商品信息并組裝整合后一次性下發給客戶端進行展示,在頁面滑動過程中不涉及接口請求。上游接口的調用方式主要分以下3種:
- 單次獲取全量商品某附屬信息: 即客戶端獲取商品基礎信息后僅調用一次上游接口,該上游接口一次性返回所有商品的某附屬信息。這種方式接口調用頻次較低且避免了部分商品附屬信息缺失的體驗問題,但是隨著購物車中商品數量的增加,對于接口的響應時長等性能挑戰也越大。
- 單次獲取部分商品某附屬信息: 即客戶端獲取商品基礎信息后僅調用一次該上游接口,但只會獲取前幾個商品的某附屬信息,其他商品的該附屬信息會缺失。這種方式減少了上游接口的調用頻次,但是犧牲了部分用戶體驗(通常是由于上游接口不支持頻繁調用,且單次計算邏輯復雜導致);
- 分批次獲取全量商品某附屬信息: 即客戶端獲取商品基礎信息后分批調用該上游接口,從而獲取所有商品的某附屬信息。這種方式避免了部分商品附屬信息缺失的體驗問題,但是上游接口的高頻次調用給上游帶來了較大的挑戰,隨著購物車中商品數量的增加,機器資源消耗也會隨之增加。
優點:對于客戶端而言交互簡單,只需關心數據刷新/變更類操作(如下拉刷新購物車、勾選反選等),一次性獲取購物車全部商品信息后整體刷新頁面,無需分析用戶滑動行為,不需要處理商品數據的組裝整合,邏輯簡單輕量。
缺點:客戶端每次觸發數據刷新/變更類操作,除了從后端獲取購物車全部商品基本信息外還需要通過異步并發框架分批請求全部商品的附屬信息,直接導致購物車整體流量翻倍,增加機器資源成本。
商品附屬信息分頁加載方案: 客戶端從后端獲取商品基礎信息后,對商品進行頁碼劃分,然后同步并行請求第1頁至屏幕瀏覽當前頁的商品附屬信息,組裝整合后下發給客戶端展示;其他頁碼的商品附屬信息由客戶端在列表滑動過程中逐頁預加載,將返回的該頁商品附屬信息與商品基礎信息組裝整合后展示。下圖對商品附屬信息分頁加載方案中購物車客戶端以及各上游接口的整體交互流程進行了清晰的說明,整體詳細的步驟為:
- 調用查詢接口時將主商品所在頁碼的pageSize傳遞給服務器,服務器將pageSize所在頁的主商品的附屬信息下發,客戶端渲染
- 將商品的所有附屬信息封裝為一個獨立接口
- 在主商品上進行打戳,標志預加載的請求時機。此處的打戳標識是根據埋點數據和用戶跟蹤獲取到的預加載標志,既能保證獨立的附屬信息接口不會有大量無效的加載,同時能夠保證附屬信息接口的數據及時更新到頁面上,確保用戶體驗
優點:商品附屬信息分頁加載方案,將用戶的刷新/變更操作和滑動操作進行行為差異細分。通過將各頁商品的附屬信息后置到滑動過程中獲取,大幅緩解了單次刷新/變更操作中上游接口集中分批調用帶來的流量性能壓力,起到日常流量削峰的作用,同時節省了未瀏覽商品的附屬信息異步接口調用(30+%),節約了對應流量的機器資源成本。
缺點:對于客戶端而言交互復雜,不僅需要關注購物車商品的刷新/變更,同時需要在滑動過程中關注上一頁/下一頁/當前頁商品附屬信息是否完整,針對附屬信息缺失的商品適時進行預加載,并對購物車主數據進行組裝整合處理。
圖3 商品附屬信息分頁加載方案
04 技術難點與解決方案
理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板后進行模板加載,加載階段會將產物轉換為視圖樹的結構,轉換完成后將通過表達式引擎解析表達式并取得正確的值,通過事件解析引擎解析用戶自定義事件并完成事件的綁定,完成解析賦值以及事件綁定后進行視圖的渲染,最終將目標頁面展示到屏幕。
1)購物車動態、多維分堆規則上移
目前購物車后端對主數據進行不同維度排序、歸堆、分類展示。其中后端服務先對購物車主數據進行店鋪歸堆、促銷歸堆、時間排序處理,客戶端對購物車主數據又進行業務精細化篩選、歸堆、排序處理(涉及30天加車、降價、常買、跨店滿減、分類等10+個業務場景)。客戶端需要對商品篩選、歸堆、排序邏輯進行統一收口處理,在此基礎上對購物車主數據進行分頁。
2 )分頁策略選型
商品分頁: 從商品維度進行分頁,n個商品為一頁。由于購物車層級結構比較復雜(店鋪-促銷-套裝/組套-商品),從商品維度進行分頁會導致店鋪、促銷、套裝被拆分,影響到購物車中店鋪、促銷、套裝業務邏輯完整性,不能滿足購物車復雜的層級結構和業務場景。
店鋪分頁: 從店鋪維度進行分頁,n個店鋪為一頁。由于單個店鋪下的商品數量差異過大,從店鋪維度進行分頁會導致每一頁的商品數量差異過大,而上游異步接口是從商品維度進行分批調用的,主數據分頁和上游異步接口分批口徑不一致,會導致通過分頁減少上游接口調用的效果大打折扣。
商品+店鋪分頁: 從商品維度進行分頁,n個商品為一頁,但是不拆分店鋪,同一個店鋪的商品歸為同一頁。這個分頁策略完美地解決了上述兩種分頁方式帶來的問題,既可以避免由于店鋪、促銷、套裝拆分而影響到店鋪、促銷、套裝維度附屬信息業務場景,又能通過靈活調控頁大小與上游接口分批調用的口徑達成一致,進而結合用戶瀏覽行為,將通過分頁減少上游接口調用價值最大化。
3 )預加載方案分析
傳統意義上的分頁通常是對主數據進行分頁,不存在數據不完整的情況,僅需要在滑動過程中加載下一頁數據。而這里的分頁是在主數據完整的情況下針對附屬信息進行分頁加載,可能會發生列表滑動過程中主數據展示不完整的情況,同時由于購物車特殊業務場景(比如錨點業務、商品順序變化等)可能會導致當前頁或前幾頁的商品附屬信息不完整,所以需要同時考慮預加載上一頁、下一頁、當前頁的交互場景。
如果不考慮預加載的方案,滑動到當前頁再加載當前頁的商品附屬信息,分頁異步接口返回后會有信息重組整合后重刷頁面的操作,從而出現頁面閃爍的情況,影響用戶體驗。
然而如果將預加載時機太前置,雖然會解決大部分頁面閃爍的問題,但會在一定程度上多請求上一頁/下一頁的異步接口,削減通過分頁加載減少上游接口調用的價值。
為了解決上述兩個問題,這里設計了預加載時機配置化方案。服務端通過將上一頁/下一頁的預加載時機配置下發,在線上靈活配置調優,以達到兼顧用戶體驗和減少上游異步接口調用的最佳平衡,從而將分頁價值最大化。
4 )分頁接口的高效調用
用戶在頁面上滑動時,有很多情況。當用戶快速滑動時,事實上對滑動過程中的內容是不關心的,只關心滾動結束處的內容,那么用戶不關心的內容可以不加載;當用戶慢速滑動時,沒有必要過早的提前預加載。針對不同的滑動場景,怎么才能在保證用戶體驗的前提下合理調用分頁附屬信息接口?
首先,我們根據用戶滑動速度有選擇的加載分頁附屬信息接口,當用戶滑動過快時不進行接口請求和渲染。其次,當用戶滑動較慢時選擇較小的預加載閾值。
5)分頁接口的臟數據處理
試想在分頁接口異步加載的過程中,頁面上的基礎數據發生了變化,此時的所有操作都是徒勞的。此種情況不僅會嚴重影響性能,更嚴重的還會導致頁面展示數據的錯誤,怎么進行臟數據的處理呢?
提到客戶端的臟數據處理,很多人都有可能想到鎖、信號量,然而鎖和信號量并不適用于這個場景,這里將介紹一種更輕量級的實現方案。首先在當前主數據請求后記一個時間戳,在每次異步接口請求前獲取到主數據的時間戳,在接口返回后再拿著接口請求前的時間戳和主數據的時間戳進行對比,如果不一致,那么此次的數據為臟數據,就進行丟棄,以此來防止臟數據問題。
05 收益
理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板后進行模板加載,加載階段會將產物轉換為視圖樹的結構,轉換完成后將通過表達式引擎解析表達式并取得正確的值,通過事件解析引擎解析用戶自定義事件并完成事件的綁定,完成解析賦值以及事件綁定后進行視圖的渲染,最終將目標頁面展示到屏幕。
圖4 分頁收益度量方案
整體的購物車附屬信息異步分頁方案已經上線運行,對方案落地后的影響和收益也進行了多維度的度量,整體也達到了我們對該方案的預期,在用戶體驗無感知的情況下完成了全鏈路流量的節省以及對業務發展更靈活的支撐;分頁改造后對比改造前單接口節省約30%的調用量,按大促場景是日常場景峰值流量的幾十倍推算,該流量在大促時刻對資源成本的節省比日常的收益大得多,并且端上異步分頁探索落地也給后續逐步疊加的附屬業務提供了一套可復用、低成本的支撐方案,讓業務落地時不用再因為購物車大容量消耗資源而放棄或降級,并且也可以驅動從歷史全車計算的重邏輯中拆離一些無需前置計算的邏輯到異步分頁中,達到渲染多少計算多少的細粒度計算效果,最大限度降低購物車在交易鏈路中的資源占用,讓京東購物車更低碳的運行。
京東購物車能否從主數據的源頭上完成分頁?能否在業務對購物車有更大容量訴求時順暢的支撐?能否在整體鏈路上不因擴容帶來資源成本的增加?后續我們會從業務轉化、用戶體驗、資源成本等角度做更多的實驗和衡量,充分將技術結合業務去探索成本、業務支撐、用戶體驗的最優解。
隨著京東購物車應用場景的豐富化和加車渠道的多元化,京東購物車的商品容量從2015年至今一直在逐步增加。
浙公網安備 33010602011771號