京東購物車分頁方案探索和落地

導讀

本文主要結合京東購物車的特性,從技術和業務層面綜合考量,探索商品附屬信息分頁加載方案,爲逐步擴容的購物車訴求做好底層技術支撐。通過本文,讀者可以充分了解到主數據分頁加載和附屬信息分頁加載分別適用哪些業務場景。在實際開發過程中,結合應用特性選擇合適的分頁技術方案,保證應用低碳高效運行。

背景

在今年的敏捷團隊建設中,我通過 Suite 執行器實現了一鍵自動化單元測試。Juint 除了 Suite 執行器還有哪些執行器呢?由此我的 Runner 探索之旅開始了!

隨着京東購物車應用場景的豐富化和加車渠道的多元化,京東購物車的商品容量從 2015 年至今一直在逐步增加。2015 年京東購物車由 80 件擴容到 120 件;2018 年由 120 件擴容到 150 件;2020 年由 150 件擴容到 180 件;2021 年京東 PLUS 會員擴容到了 220 件。持續不斷的擴容給我們的後端服務帶來了巨大的負載壓力,因爲用戶購物車中商品種類數量的增加對應到後端的計算資源也會線性增加,如何做到資源最大限度的節約又能保證業務和用戶的體驗不受影響,如何從技術和業務層面綜合考量爲逐步擴容的購物車訴求做好底層的支撐,一直以來都是擺在我們面前的一個痛點和挑戰。

首先描述下京東購物車的特性:用戶在購物車操作完商品後會記錄下用戶當前的操作狀態,比如勾選,反勾選,切換促銷後的商品促銷信息等,當用戶再次進入購物車後會全量獲取購物車中的所有商品,根據商品的勾選態,促銷等實時計算商品的價格,展示給用戶。每次刷新或者修改購物車商品都是全量數據下發。持續擴容勢必會持續加大後端服務的壓力,同時購物車頁面的佈局計算、渲染等操作不僅使用戶等待頁面刷新的時間變長,而且還會佔用大量的內存資源,導致手機卡頓。

京東購物車爲了提升用戶體驗,保留了勾選商品總額、優惠促銷、運費等一系列整車維度的計算邏輯,最終導致目前無法一步到位去實現購物車主商品的分頁。期間也對行業內的主流電商類 APP 做了充分的調研,大部分 APP 都沒有做購物車分頁且購物車容量上限也大都控制在 120 以下,做了分頁的 APP 也在勾選態保留和全局優惠計算等方面做了一些簡化和降級,所以我們決定從另一個方向進行探索和突破,即商品附屬信息分頁,暫時避開會影響到全局優惠計算和影響業務玩法、流量轉化的主數據分頁。

什麼是商品基礎信息和附屬信息?商品基礎信息和商品附屬信息的劃分主要從上游接口層面進行區分,商品基礎信息即從購物車中臺直接獲取的商品信息,比如商品圖片、商品名稱、商品價格、商品類型等;基於基礎信息,通過異步並行框架分批獲取的商品的附屬信息,比如優惠券、預估到手價、商品庫存、活動標籤、服務、秒殺、閃購等。

圖 1 商品信息示例

目標

理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板後進行模板加載,加載階段會將產物轉換爲視圖樹的結構,轉換完成後將通過表達式引擎解析表達式並取得正確的值,通過事件解析引擎解析用戶自定義事件並完成事件的綁定,完成解析賦值以及事件綁定後進行視圖的渲染,最終將目標頁面展示到屏幕

  1. 提升用戶體驗,解決由於上游服務接口無法支撐購物車超多商品併發訪問而導致的產品體驗問題,在無損用戶體驗的情況下,保證用戶在購物車滑動過程中無感知分頁加載商品附屬信息;

  2. 縮減機器成本,減少不必要的上游接口請求,降低後端服務器負載;

技術方案

理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板後進行模板加載,加載階段會將產物轉換爲視圖樹的結構,轉換完成後將通過表達式引擎解析表達式並取得正確的值,通過事件解析引擎解析用戶自定義事件並完成事件的綁定,完成解析賦值以及事件綁定後進行視圖的渲染,最終將目標頁面展示到屏幕。從設計稿出發,提升頁面搭建效率,亟需解決的核心問題有:

1)商品附屬信息分頁加載價值分析

根據購物車線上不同維度埋點數據分析結果顯示,京東購物車中商品數量在 20-220 區間的請求次數佔總請求次數一半以上,平均一屏展示的商品數量不超過 3 個,購物車中商品瀏覽的平均曝光深度 6~7 個,由此分析大部分的上游接口調用都有很大節省空間。通過前端線上模擬分頁埋點分析預估,商品附屬信息分頁調用的方式可以減少 30+% 的上游異步接口調用,做到在無損用戶體驗的情況下,削減接口調用峯值,降低接口的性能壓力和機器資源消耗。

2)商品附屬信息分頁加載

商品附屬信息分頁前後接口交互的差異在下圖進行了清晰的標識,主要體現在頁面刷新和頁面滑動兩個方面。

圖 2 異步請求分頁方案

商品附屬信息不分頁加載方案: 客戶端觸發一次刷新操作需要從各個上游接口獲取所有商品信息並組裝整合後一次性下發給客戶端進行展示,在頁面滑動過程中不涉及接口請求。上游接口的調用方式主要分以下 3 種:

優點:對於客戶端而言交互簡單,只需關心數據刷新 / 變更類操作(如下拉刷新購物車、勾選反選等),一次性獲取購物車全部商品信息後整體刷新頁面,無需分析用戶滑動行爲,不需要處理商品數據的組裝整合,邏輯簡單輕量。

缺點:客戶端每次觸發數據刷新 / 變更類操作,除了從後端獲取購物車全部商品基本信息外還需要通過異步併發框架分批請求全部商品的附屬信息,直接導致購物車整體流量翻倍,增加機器資源成本。

商品附屬信息分頁加載方案: 客戶端從後端獲取商品基礎信息後,對商品進行頁碼劃分,然後同步並行請求第 1 頁至屏幕瀏覽當前頁的商品附屬信息,組裝整合後下發給客戶端展示;其他頁碼的商品附屬信息由客戶端在列表滑動過程中逐頁預加載,將返回的該頁商品附屬信息與商品基礎信息組裝整合後展示。下圖對商品附屬信息分頁加載方案中購物車客戶端以及各上游接口的整體交互流程進行了清晰的說明,整體詳細的步驟爲:

優點:商品附屬信息分頁加載方案,將用戶的刷新 / 變更操作和滑動操作進行行爲差異細分。通過將各頁商品的附屬信息後置到滑動過程中獲取,大幅緩解了單次刷新 / 變更操作中上游接口集中分批調用帶來的流量性能壓力,起到日常流量削峯的作用,同時節省了未瀏覽商品的附屬信息異步接口調用(30+%),節約了對應流量的機器資源成本。

缺點:對於客戶端而言交互複雜,不僅需要關注購物車商品的刷新 / 變更,同時需要在滑動過程中關注上一頁 / 下一頁 / 當前頁商品附屬信息是否完整,針對附屬信息缺失的商品適時進行預加載,並對購物車主數據進行組裝整合處理。

圖 3 商品附屬信息分頁加載方案

技術難點與解決方案

理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板後進行模板加載,加載階段會將產物轉換爲視圖樹的結構,轉換完成後將通過表達式引擎解析表達式並取得正確的值,通過事件解析引擎解析用戶自定義事件並完成事件的綁定,完成解析賦值以及事件綁定後進行視圖的渲染,最終將目標頁面展示到屏幕。

1)購物車動態、多維分堆規則上移

目前購物車後端對主數據進行不同維度排序、歸堆、分類展示。其中後端服務先對購物車主數據進行店鋪歸堆、促銷歸堆、時間排序處理,客戶端對購物車主數據又進行業務精細化篩選、歸堆、排序處理(涉及 30 天加車、降價、常買、跨店滿減、分類等 10 + 個業務場景)。客戶端需要對商品篩選、歸堆、排序邏輯進行統一收口處理,在此基礎上對購物車主數據進行分頁。

2)分頁策略選型

商品分頁: 從商品維度進行分頁,n 個商品爲一頁。由於購物車層級結構比較複雜(店鋪 - 促銷 - 套裝 / 組套 - 商品),從商品維度進行分頁會導致店鋪、促銷、套裝被拆分,影響到購物車中店鋪、促銷、套裝業務邏輯完整性,不能滿足購物車複雜的層級結構和業務場景。

店鋪分頁: 從店鋪維度進行分頁,n 個店鋪爲一頁。由於單個店鋪下的商品數量差異過大,從店鋪維度進行分頁會導致每一頁的商品數量差異過大,而上游異步接口是從商品維度進行分批調用的,主數據分頁和上游異步接口分批口徑不一致,會導致通過分頁減少上游接口調用的效果大打折扣。

商品 + 店鋪分頁: 從商品維度進行分頁,n 個商品爲一頁,但是不拆分店鋪,同一個店鋪的商品歸爲同一頁。這個分頁策略完美地解決了上述兩種分頁方式帶來的問題,既可以避免由於店鋪、促銷、套裝拆分而影響到店鋪、促銷、套裝維度附屬信息業務場景,又能通過靈活調控頁大小與上游接口分批調用的口徑達成一致,進而結合用戶瀏覽行爲,將通過分頁減少上游接口調用價值最大化。

3)預加載方案分析

傳統意義上的分頁通常是對主數據進行分頁,不存在數據不完整的情況,僅需要在滑動過程中加載下一頁數據。而這裏的分頁是在主數據完整的情況下針對附屬信息進行分頁加載,可能會發生列表滑動過程中主數據展示不完整的情況,同時由於購物車特殊業務場景(比如錨點業務、商品順序變化等)可能會導致當前頁或前幾頁的商品附屬信息不完整,所以需要同時考慮預加載上一頁、下一頁、當前頁的交互場景。

如果不考慮預加載的方案,滑動到當前頁再加載當前頁的商品附屬信息,分頁異步接口返回後會有信息重組整合後重刷頁面的操作,從而出現頁面閃爍的情況,影響用戶體驗。

然而如果將預加載時機太前置,雖然會解決大部分頁面閃爍的問題,但會在一定程度上多請求上一頁 / 下一頁的異步接口,削減通過分頁加載減少上游接口調用的價值。

爲了解決上述兩個問題,這裏設計了預加載時機配置化方案。服務端通過將上一頁 / 下一頁的預加載時機配置下發,在線上靈活配置調優,以達到兼顧用戶體驗和減少上游異步接口調用的最佳平衡,從而將分頁價值最大化。

4)分頁接口的高效調用

用戶在頁面上滑動時,有很多情況。當用戶快速滑動時,事實上對滑動過程中的內容是不關心的,只關心滾動結束處的內容,那麼用戶不關心的內容可以不加載;當用戶慢速滑動時,沒有必要過早的提前預加載。針對不同的滑動場景,怎麼才能在保證用戶體驗的前提下合理調用分頁附屬信息接口?

首先,我們根據用戶滑動速度有選擇的加載分頁附屬信息接口,當用戶滑動過快時不進行接口請求和渲染。其次,當用戶滑動較慢時選擇較小的預加載閾值。

5)分頁接口的髒數據處理

試想在分頁接口異步加載的過程中,頁面上的基礎數據發生了變化,此時的所有操作都是徒勞的。此種情況不僅會嚴重影響性能,更嚴重的還會導致頁面展示數據的錯誤,怎麼進行髒數據的處理呢?

提到客戶端的髒數據處理,很多人都有可能想到鎖、信號量,然而鎖和信號量並不適用於這個場景,這裏將介紹一種更輕量級的實現方案。首先在當前主數據請求後記一個時間戳,在每次異步接口請求前獲取到主數據的時間戳,在接口返回後再拿着接口請求前的時間戳和主數據的時間戳進行對比,如果不一致,那麼此次的數據爲髒數據,就進行丟棄,以此來防止髒數據問題。

收益 

理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板後進行模板加載,加載階段會將產物轉換爲視圖樹的結構,轉換完成後將通過表達式引擎解析表達式並取得正確的值,通過事件解析引擎解析用戶自定義事件並完成事件的綁定,完成解析賦值以及事件綁定後進行視圖的渲染,最終將目標頁面展示到屏幕。

圖 4 分頁收益度量方案

整體的購物車附屬信息異步分頁方案已經上線運行,對方案落地後的影響和收益也進行了多維度的度量,整體也達到了我們對該方案的預期,在用戶體驗無感知的情況下完成了全鏈路流量的節省以及對業務發展更靈活的支撐;分頁改造後對比改造前單接口節省約 30% 的調用量,按大促場景是日常場景峯值流量的幾十倍推算,該流量在大促時刻對資源成本的節省比日常的收益大得多,並且端上異步分頁探索落地也給後續逐步疊加的附屬業務提供了一套可複用、低成本的支撐方案,讓業務落地時不用再因爲購物車大容量消耗資源而放棄或降級,並且也可以驅動從歷史全車計算的重邏輯中拆離一些無需前置計算的邏輯到異步分頁中,達到渲染多少計算多少的細粒度計算效果,最大限度降低購物車在交易鏈路中的資源佔用,讓京東購物車更低碳的運行。

京東購物車能否從主數據的源頭上完成分頁?能否在業務對購物車有更大容量訴求時順暢的支撐?能否在整體鏈路上不因擴容帶來資源成本的增加?後續我們會從業務轉化、用戶體驗、資源成本等角度做更多的實驗和衡量,充分將技術結合業務去探索成本、業務支撐、用戶體驗的最優解。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/IFRSro_ox4dQ04h2Sgl6kQ