本章以京東商品詳情頁爲例,京東商品詳情頁雖然僅是單個頁面,可是其數據聚合源是很是多的,除了一些實時性要求比較高的如價格、庫存、服務支持等經過AJAX異步加載加載以外,其餘的數據都是在後端作數據聚合而後拼裝網頁模板的。前端
如圖所示,商品頁主要包括商品基本信息(基本信息、圖片列表、顏色/尺碼關係、擴展屬性、規格參數、包裝清單、售後保障等)、商品介紹、其餘信息(分類、品牌、店鋪【第三方賣家】、店內分類【第三方賣家】、同類相關品牌)。更多細節此處就不闡述了。後端
整個京東有數億商品,若是每次動態獲取如上內容進行模板拼裝,數據來源之多足以形成性能沒法知足要求;最初的解決方案是生成靜態頁,可是靜態頁的最大的問題:一、沒法迅速響應頁面需求變動;二、很難作多版本線上對比測試。如上兩個因素足以制約商品頁的多樣化發展,所以靜態化技術不是很好的方案。緩存
經過分析,數據主要分爲四種:商品頁基本信息、商品介紹(異步加載)、其餘信息(分類、品牌、店鋪等)、其餘須要實時展現的數據(價格、庫存等)。而其餘信息如分類、品牌、店鋪是很是少的,徹底能夠放到一個佔用內存很小的Redis中存儲;而商品基本信息咱們能夠借鑑靜態化技術將數據作聚合存儲,這樣的好處是數據是原子的,而模板是隨時可變的,吸取了靜態頁聚合的優勢,彌補了靜態頁的多版本缺點;另一個很是嚴重的問題就是嚴重依賴這些相關係統,若是它們掛了或響應慢則商品頁就掛了或響應慢;商品介紹咱們也經過AJAX技術惰性加載(由於是第二屏,只有當用戶滾動鼠標到該屏時才顯示);而實時展現數據經過AJAX技術作異步加載;所以咱們能夠作以下設計:架構
接收商品變動消息,作商品基本信息的聚合,即從多個數據源獲取商品相關信息如圖片列表、顏色尺碼、規格參數、擴展屬性等等,聚合爲一個大的JSON數據作成數據閉環,以key-value存儲;由於是閉環,即便依賴的系統掛了咱們商品頁仍是能繼續服務的,對商品頁不會形成任何影響;
接收商品介紹變動消息,存儲商品介紹信息;
介紹其餘信息變動消息,存儲其餘信息。異步
整個架構以下圖所示:性能
技術選型
MQ可使用如Apache ActiveMQ;
Worker/動態服務能夠經過如Java技術實現;
RPC能夠選擇如alibaba Dubbo;
KV持久化存儲能夠選擇SSDB(若是使用SSD盤則能夠選擇SSDB+RocksDB引擎)或者ARDB(LMDB引擎版);
緩存使用Redis;
SSDB/Redis分片使用如Twemproxy,這樣無論使用Java仍是Nginx+Lua,它們都不關心分片邏輯;
前端模板拼裝使用Nginx+Lua;
數據集羣數據存儲的機器能夠採用RAID技術或者主從模式防止單點故障;
由於數據變動不頻繁,能夠考慮SSD替代機械硬盤測試
核心流程
首先咱們監聽商品數據變動消息;
接收到消息後,數據聚合Worker經過RPC調用相關係統獲取全部要展現的數據,此處獲取數據的來源可能很是多並且響應速度徹底受制於這些系統,可能耗時幾百毫秒甚至上秒的時間;
將數據聚合爲JSON串存儲到相關數據集羣;
前端Nginx經過Lua獲取相關集羣的數據進行展現;商品頁須要獲取基本信息+其餘信息進行模板拼裝,即拼裝模板僅須要兩次調用(另外由於其餘信息數據量少且對一致性要求不高,所以咱們徹底能夠緩存到Nginx本地全局內存,這樣能夠減小遠程調用提升性能);當頁面滾動到商品介紹頁面時異步調用商品介紹服務獲取數據;
若是從聚合的SSDB集羣/Redis中獲取不到相關數據;則回源到動態服務經過RPC調用相關係統獲取全部要展現的數據返回(此處能夠作限流處理,由於若是大量請求過來的話可能致使服務雪崩,須要採起保護措施),此處的邏輯和數據聚合Worker徹底同樣;而後發送MQ通知數據變動,這樣下次訪問時就能夠從聚合的SSDB集羣/Redis中獲取數據了。設計