天天看點

「後端」京東百億規模實時浏覽記錄系統的設計與實作

作者:架構思考

一、系統介紹

浏覽記錄系統主要用來記錄京東使用者的實時浏覽記錄,并提供實時查詢浏覽資料的功能。線上使用者通路一次商品詳情頁,浏覽記錄系統就會記錄使用者的一條浏覽資料,并針對該浏覽資料進行商品次元去重等一系列處理并存儲。然後使用者可以通過我的京東或其他入口查詢使用者的實時浏覽商品記錄,實時性可以達到毫秒級。目前本系統可以為京東每個使用者提供最近 200 條的浏覽記錄查詢展示。

二、系統設計與實作

2.1 系統整體架構設計

「後端」京東百億規模實時浏覽記錄系統的設計與實作

整個系統架構主要分為四個子產品,包括浏覽資料存儲子產品、浏覽資料查詢子產品、浏覽資料實時上報子產品和浏覽資料離線上報子產品:

  • 浏覽資料存儲子產品:主要用來存儲京東使用者的浏覽曆史記錄,目前京東有近 5 億的活躍使用者,按照每個使用者保留最少 200 條的浏覽曆史記錄,需要設計存儲近千億條的使用者浏覽曆史資料;
  • 浏覽資料查詢子產品:主要為前台提供微服務接口,包括查詢使用者的浏覽記錄總數量,使用者實時浏覽記錄清單和浏覽記錄的删除操作等功能;
  • 浏覽資料實時上報子產品:主要處理京東所有線上使用者的實時 PV 資料,并将該浏覽資料存儲到實時資料庫;
  • 浏覽資料離線上報子產品:主要用來處理京東所有使用者的 PV 離線資料,将使用者曆史 PV 資料進行清洗,去重和過濾,最後将浏覽資料推送到離線資料庫中。

2.1.1 資料存儲子產品設計與實作

考慮到需要存儲近千億條的使用者浏覽記錄,并且還要滿足京東線上使用者的毫秒級浏覽記錄實時存儲和前台查詢功能,我們将浏覽曆史資料進行了冷熱分離。Jimdb 純記憶體操作,存取速度快,是以我們将使用者的(T-4)浏覽記錄資料存儲到 Jimdb 的記憶體中,可以滿足京東線上活躍使用者的實時存儲和查詢。而(T+4)以外的離線浏覽資料則直接推送到 Hbase 中,存儲到磁盤上,用來節省存儲成本。如果有不活躍的使用者查詢到了冷資料,則将冷資料複制到 Jimdb 中,用來提高下一次的查詢性能。

熱資料采用了 JIMDB 的有序集合來存儲使用者的實時浏覽記錄,使用使用者名做為有序集合的 KEY,浏覽商品 SKU 作為有序集合的元素,浏覽商品的時間戳作為元素的分數,然後針對該 KEY 設定過期時間為 4 天。

「後端」京東百億規模實時浏覽記錄系統的設計與實作

這裡的熱資料過期時間為什麼選擇 4 天?

這是因為我們的大資料平台離線浏覽資料都是 T+1 上報彙總的,等我們開始處理使用者的離線浏覽資料的時候已經是第二天,在加上我們自己的業務流程處理和資料清洗過濾過程,到最後推送到 Hbase 中,也需要執行消耗十幾個小時。是以熱資料的過期時間最少需要設定 2 天,但是考慮到大資料任務執行失敗和重試的過程,需要預留 2 天的任務重試和資料修複時間,是以熱資料過期時間設定為 4 天。是以當使用者 4 天内都沒有浏覽新商品時,使用者檢視的浏覽記錄則是直接從 Hbase 中查詢展示。

冷資料則采用 K-V 格式存儲使用者浏覽資料,使用使用者名作為 KEY,使用者浏覽商品和浏覽時間對應 Json 字元串做為 Value 進行存儲,存儲時需要保證使用者的浏覽順序,避免進行二次排序。其中使用使用者名做 KEY 時,由于大部分使用者名都有相同的字首,會出現資料傾斜問題,是以我們針對使用者名進行了 MD5 處理,然後截取 MD5 後的中間四位作為 KEY 的字首,進而解決了 Hbase 的資料傾斜問題。最後在針對 KEY 設定過期時間為 62 天,實作離線資料的過期自動清理功能。

「後端」京東百億規模實時浏覽記錄系統的設計與實作

2.1.2 查詢服務子產品設計與實作

查詢服務子產品主要包括三個微服務接口,包括查詢使用者浏覽記錄總數量,查詢使用者浏覽記錄清單和删除使用者浏覽記錄接口。

  • 查詢使用者浏覽記錄總數量接口設計面臨的問題

1. 如何解決限流防刷問題?

基于 Guava 的 RateLimiter 限流器和 Caffeine 本地緩存實作方法全局、調用方和使用者名三個次元的限流。具體政策是當調用發第一次調用方法時,會生成對應次元的限流器,并将該限流器儲存到 Caffeine 實作的本地緩存中,然後設定固定的過期時間,當下一次調用該方法時,生成對應的限流 key 然後從本地緩存中擷取對應的限流器,該限流器中保留着該調用方的調用次數資訊,進而實作限流功能。

2. 如何查詢使用者浏覽記錄總數量?

首先查詢使用者浏覽記錄總數緩存,如果緩存命中,直接傳回結果,如果緩存未命中則需要從 Jimdb 中查詢使用者的實時浏覽記錄清單,然後在批量補充商品資訊,由于使用者的浏覽 SKU 清單可能較多,此處可以進行分批查詢商品資訊,分批數量可以動态調整,防止因為一次查詢商品數量過多而影響查詢性能。

由于前台展示的浏覽商品清單需要針對同一 SPU 商品進行去重,是以需要補充的商品資訊字段包括商品名稱、商品圖檔和商品 SPUID 等字段。針對 SPUID 字段去重後,在判斷是否需要查詢 Hbase 離線浏覽資料,此處可以通過離線查詢開關、使用者清空标記和 SPUID 去重後的浏覽記錄數量來判斷是否需要查詢 Hbase 離線浏覽記錄。

如果去重後的時候浏覽記錄數量已經滿足系統設定的使用者最大浏覽記錄數量,則不再查詢離線記錄。如果不滿足則繼續查詢離線的浏覽記錄清單,并與使用者的實時浏覽記錄清單進行合并,并過濾掉重複的浏覽 SKU 商品。

擷取到使用者完整的浏覽記錄清單後,在過濾掉使用者已經删除的浏覽記錄,然後 count 清單的長度,并與系統設定的使用者最大浏覽記錄數量做比較取最小值,就是該使用者的浏覽記錄總數量,擷取到使用者浏覽記錄總數量後可以根據緩存開關來判斷是否需要異步寫入使用者總數量緩存。

「後端」京東百億規模實時浏覽記錄系統的設計與實作

3. 查詢使用者浏覽記錄清單

查詢使用者浏覽記錄清單流程與查詢使用者浏覽記錄總數量流程基本一緻。

2.1.2 浏覽資料實時上報子產品設計與實作

「後端」京東百億規模實時浏覽記錄系統的設計與實作

商詳服務端将使用者的實時浏覽資料通過 Kafka 用戶端上報到 Kafka 叢集的消息隊列中,為了提高資料上報性能,使用者浏覽資料主題分成了 50 個分區,Kafka 可以将使用者的浏覽消息均勻的分散到 50 個分區隊列中,進而大大提升了系統的吞吐能力。

浏覽記錄系統則通過 Flink 叢集來消費 Kafka 隊列中的使用者浏覽資料,然後将浏覽資料實時存儲到 Jimdb 記憶體中。Flink 叢集不僅實作了橫向動态擴充,進一步提高 Flink 叢集的吞吐能力,防止出現消息積壓,還保證了使用者的浏覽消息恰好消費一次,在異常發生時不會丢失使用者資料并能自動恢複。Flink 叢集存儲實作使用 Lua 腳本合并執行 Jimdb 的多個指令,包括插入 sku、判斷 sku 記錄數量,删除 sku 和設定過期時間等,将多次網絡 IO 操作優化為 1 次。

為什麼選擇 Flink 流式處理引擎和 Kafka,而不是商詳服務端直接将浏覽資料寫入到 Jimdb 記憶體中呢?

首先,京東商城做為一個 7x24 小時服務的電子商務網站,并且有着 5 億 + 的活躍使用者,每一秒中都會有使用者在浏覽商品詳情頁,就像是流水一樣,源源不斷,非常符合分布式流式資料處理的場景。

而相對于其他流式處理架構,Flink 基于分布式快照的方案在功能和性能方面都具有很多優點,包括:

  • 低延遲:由于操作符狀态的存儲可以異步,是以進行快照的過程基本上不會阻塞消息的處理,是以不會對消息延遲産生負面影響。
  • 高吞吐量:當操作符狀态較少時,對吞吐量基本沒有影響。當操作符狀态較多時,相對于其他的容錯機制,分布式快照的時間間隔是使用者自定義的,是以使用者可以權衡錯誤恢複時間和吞吐量要求來調整分布式快照的時間間隔。
  • 與業務邏輯的隔離:Flink 的分布式快照機制與使用者的業務邏輯是完全隔離的,使用者的業務邏輯不會依賴或是對分布式快照産生任何影響。
  • 錯誤恢複代價:分布式快照的時間間隔越短,錯誤恢複的時間越少,與吞吐量負相關。

第二,京東每天都會有很多的秒殺活動,比如茅台搶購,預約使用者可達上百萬,在同一秒鐘就會有上百萬的使用者重新整理商詳頁面,這樣就會産生流量洪峰,如果全部實時寫入,會對我們的實時存儲造成很大的壓力,并且影響前台查詢接口的性能。是以我們就利用 Kafka 來進行削峰處理,而且也對系統進行了解耦處理,使得商詳系統可以不強制依賴浏覽記錄系統。

這裡為什麼選擇 Kafka?

這裡就需要先了解 Kakfa 的特性。

  • 高吞吐、低延遲:kakfa 最大的特點就是收發消息非常快,kafka 每秒可以處理幾十萬條消息,它的最低延遲隻有幾毫秒。
  • 高伸縮性: 每個主題 (topic) 包含多個分區 (partition),主題中的分區可以分布在不同的主機 (broker) 中。
  • 持久性、可靠性: Kafka 能夠允許資料的持久化存儲,消息被持久化到磁盤,并支援資料備份防止資料丢失,Kafka 底層的資料存儲是基于 Zookeeper 存儲的,Zookeeper 我們知道它的資料能夠持久存儲。
  • 容錯性: 允許叢集中的節點失敗,某個節點當機,Kafka 叢集能夠正常工作。
  • 高并發: 支援數千個用戶端同時讀寫。

Kafka 為什麼這麼快?

  • Kafka 通過零拷貝原理來快速移動資料,避免了核心之間的切換。
  • Kafka 可以将資料記錄分批發送,從生産者到檔案系統到消費者,可以端到端的檢視這些批次的資料。
  • 批處理的同時更有效的進行了資料壓縮并減少 I/O 延遲。
  • Kafka 采取順序寫入磁盤的方式,避免了随機磁盤尋址的浪費。

目前本系統已經經曆多次大促考驗,且系統沒有進行降級,使用者的實時浏覽消息沒有積壓,基本實作了毫秒級的處理能力,方法性能 TP999 達到了 11ms。

2.1.3 浏覽資料離線上報子產品設計與實作

「後端」京東百億規模實時浏覽記錄系統的設計與實作

離線資料上報處理流程如下:

  1. 商詳前端通過子午線的 API 将使用者的 PV 資料進行上報,子午線将使用者的 PV 資料寫入到資料集市的使用者 PV 分區表中。
  2. 資料抽數任務每天淩晨 2 點 33 分從浏覽記錄系統 Mysql 庫的使用者已删除浏覽記錄表抽數到資料集市,并将删除資料寫入到使用者删除浏覽記錄表。
  3. 離線資料計算任務每天上午 11 點開始執行,先從使用者 PV 分區表中提取近 60 天、每人 200 條的去重資料,然後根據使用者删除浏覽記錄表過濾删除資料,并計算出當天新增或者删除過的使用者名,最後存儲到離線資料分區表中。
  4. 離線資料出庫任務每天淩晨 2 點從離線資料分區表中将 T+2 的增量離線浏覽資料經過資料清洗和格式轉換,将 T+2 活躍使用者的 K-V 格式離線浏覽資料推送到 Hbase 叢集。

文章來源:曹志飛_京東雲開發者社群

繼續閱讀