一般的電商演變:
商品詳情頁系統架構演進歷程
第一個版本
架構設計
J2EE+Tomcat+MySQL
動態頁面,每次請求都要調用多個依賴服務的接口,從數據庫里查詢數據,然后通過類似JSP的技術渲染到HTML模板中,返回最終HTML頁面
架構缺陷
每次請求都是要訪問數據庫的,性能肯定很差
每次請求都要調用大量的依賴服務,依賴服務不穩定導致商品詳情頁展示的性能經常抖動
第二個版本
架構設計
頁面靜態化技術
通過MQ得到商品詳情頁涉及到的數據的變更消息
通過Java Worker服務全量調用所有的依賴服務的接口,查詢數據庫,獲取到構成一個商品詳情頁的完整數據,並通過velocity等模板技術生成靜態HTML
將靜態HTML頁面通過rsync工具直接推送到多台nginx服務器上,每台nginx服務器上都有全量的HTML靜態頁面
nginx對商品詳情頁的訪問請求直接返回本地的靜態HTML頁面
在nginx服務器前加一層負載均衡設備,請求打到任何一台應用nginx服務器上,都有全量的HTML靜態頁面可以返回
架構缺陷
全量更新問題
如果某一個商品分類、商家等信息變更了
那么那個分類、店鋪、商家下面所有的商品詳情頁都需要重新生成靜態HTML頁面
更新速度過慢問題
分類、店鋪、商家、商品越來越多
重新生成HTML的負載越來越高,rsync全量同步所有nginx的負載也越來越高
從數據變更到生成靜態HTML,再到全量同步到所有nginx,時間越來越慢
擴容問題
因為每個商品詳情頁都要全量同步到所有的nginx上,導致系統無法擴容,無法增加系統容量
架構優化
解決全量更新問題
每次Java Worker收到某個維度的變更消息,不是拉去全量維度並生成完整HTML,而是按照維度拆分,生成一個變化維度的HTML片段
nginx對多個HTML片段通過SSI合並html片段然后輸出一個完整的html
解決擴容問題
每個商品詳情頁不是全量同步到所有的nginx
而是根據商品id路由到某一台nginx上,同時接入層nginx按照相同的邏輯路由請求
更新速度過慢問題
增加更多機器資源
多機房部署,每個機房部署一套Java Worker+應用Nginx,所有機房用一套負載均衡設備,在每個機房內部完成全流程,不跨機房
架構優化后的缺陷
更新速度還是不夠快的問題
商品的每個維度都有一個HTML片段,rsync推送大量的HTML片段,負載太高,性能較差
Nginx基於機械硬盤進行SSI合並,性能太差
還是存在全量更新的問題
雖然解決了分類、商家、店鋪維度的變更,只要增量重新生產較小的HTML片段即可,不用全量重新生成關聯的所有商品詳情頁的HTML
但是如果某個頁面模板變更,或者新加入一個頁面模板,還是會導致幾億個商品的HTML片段都要重新生成和rsync,要幾天時間才能完成,無法響應需求
還是存在容量問題
nginx存儲有限,不能無限存儲幾億,以及增長的商品詳情頁的HTML文件
如果nginx存儲達到極限,需要刪除部分商品詳情頁的HTML文件,改成nginx找不到HTML,則調用后端接口,回到動態頁面的架構
動態頁面架構在高並發訪問的情況下,會對依賴系統造成過大的壓力,幾乎扛不住
第三個版本
需要支持的需求
迅速響應各種頁面模板的改版和個性化需求的新模板的加入
頁面模塊化,頁面中的某個區域變化,只要更新這個區域中的數據即可
支持高性能訪問
支持水平擴容的伸縮性架構
架構設計
系統架構設計
依賴服務有數據變更發送消息到MQ
數據異構Worker服務監聽MQ中的變更消息,調用依賴服務的接口,僅僅拉取有變更的數據即可,然后將數據存儲到redis中
數據異構Worker存儲到redis中的,都是原子未加工數據,包括商品基本信息、商品擴展屬性、商品其他信息、商品規格參數、商品分類、商家信息
數據異構Worker發送消息到MQ,數據聚合Worker監聽到MQ消息
數據聚合Worker將原子數據從redis中取出,按照維度聚合后存儲到redis中,包括三個維度
基本信息維度:基本信息、擴展屬性
商品介紹:PC版、移動版
其他信息:商品分類、商家信息
nginx+lua,lua從redis讀取商品各個維度的數據,通過nginx動態渲染到html模板中,然后輸出最終的html
如何解決所有的問題
更新問題:不再是生成和推送html片段了,不再需要合成html,直接數據更新到redis,然后走動態渲染,性能大大提升
全量更新問題:數據和模板分離,數據更新呢就更新數據,模板更新直接推送模板到nginx,不需要重新生成所有html,直接走動態渲染
容量問題:不需要依賴nginx所在機器的磁盤空間存儲大量的html,將數據放redis,html就存放模板,大大減少空間占用,而且redis集群可擴容
商品詳情頁整體架構組成
動態渲染系統
將頁面中靜的數據,直接在變更的時候推送到緩存,然后每次請求頁面動態渲染新數據
商品詳情頁系統(負責靜的部分):被動接收數據,存儲redis,nginx+lua動態渲染
商品詳情頁動態服務系統(對外提供數據接口)
提供各種數據接口
動態調用依賴服務的接口,產生數據並且返回響應
從商品詳情頁系統處理出來的redis中,獲取數據,並返回響應
OneService系統
動的部分,都是走ajax異步請求的,不是走動態渲染的
商品詳情頁統一服務系統(負責動的部分)
前端頁面
靜的部分,直接被動態渲染系統渲染進去了
動的部分,html一到瀏覽器,直接走js腳本,ajax異步加載
商品詳情頁,分段存儲,ajax異步分屏加載
工程運維
限流,壓測,灰度發布
多級緩存架構
本地緩存
使用nginx shared dict作為local cache,http-lua-module的shared dict可以作為緩存,而且reload nginx不會丟失
也可以使用nginx proxy cache做local cache
雙層nginx部署,一層接入,一層應用,接入層用hash路由策略提升緩存命中率
比如庫存緩存數據的TP99為5s,本地緩存命中率25%,redis命中率28%,回源命中率47%
一次普通秒殺活動的命中率,本地緩存55%,分布式redis命中率15%,回源命中率27%
最高可以提升命中率達到10%
全緩存鏈路維度化存儲,如果有3個維度的數據,只有其中1個過期了,那么只要獲取那1個過期的數據即可
nginx local cache的過期時間一般設置為30min,到后端的流量會減少至少3倍
4級多級緩存
nginx本地緩存,抗熱點數據,小內存緩存訪問最頻繁的數據
各個機房本地的redis從集群的數據,抗大量離線數據,采用一致性hash策略構建分布式redis緩存集群
tomcat中的動態服務的本地jvm堆緩存
支持在一個請求中多次讀取一個數據,或者與該數據相關的數據
作為redis崩潰的備用防線
固定緩存一些較少訪問頻繁的數據,比如分類,品牌等數據
堆緩存過期時間為redis過期時間的一半
主redis集群
命中率非常低,小於5%
防止主從同步延遲導致的數據讀取miss
防止各個機房的從redis集群崩潰之后,全量走依賴服務會導致雪崩,主redis集群是后備防線
主redis集群,采取多機房一主三從的高可用部署架構
redis集群部署采取雙機房一主三活的架構,機房A部署主集群+一個從集群,機房B部署一個從集群(從機房A主集群)+一個從集群(從機房B從集群)
雙機房一主三活的架構,保證了機房A徹底故障的時候,機房B還有一套備用的集群,可以升級為一主一從
如果采取機房A部署一主一從,機房B一從,那么機房A故障時,機房B的一從承載所有讀寫壓力,壓力過大,很難承受
動態渲染那套系統
(1)依賴服務 -> MQ -> 動態渲染服務 -> 多級緩存
(2)負載均衡 -> 分發層nginx -> 應用層nginx -> 多級緩存
(3)多級緩存 -> 數據直連服務
動態渲染系統
數據閉環
數據閉環架構
依賴服務:商品基本信息,規格參數,商家/店鋪,熱力圖,商品介紹,商品維度,品牌,分類,其他
發送數據變更消息到MQ
數據異構Worker集群,監聽MQ,將原子數據存儲到redis,發送消息到MQ
數據聚合Worker集群,監聽MQ,將原子數據按維度聚合后存儲到redis,三個維度(商品基本信息、商品介紹、其他信息)
數據閉環,就是數據的自我管理,所有數據原樣同步后,根據自己的邏輯進行后續的數據加工,走系統流程,以及展示k
數據形成閉環之后,依賴服務的抖動或者維護,不會影響到整個商品詳情頁系統的運行
數據閉環的流程:數據異構(多種異構數據源拉取),數據原子化,數據聚合(按照維度將原子數據進行聚合),數據存儲(Redis)
數據維度化
商品基本信息:標題、擴展屬性、特殊屬性、圖片、顏色尺碼、規格參數
商品介紹
非商品維度其他信息:分類,商家,店鋪,品牌
商品維度其他信息:采用ajax異步加載,價格,促銷,配送至,廣告,推薦,最佳組合,等等
采取ssdb,這種基於磁盤的大容量/高性能的kv存儲,保存商品維度、主商品維度、商品維度其他信息,數據量大,不能光靠內存去支撐
采取redis,純內存的kv存儲,保存少量的數據,比如非商品維度的其他數據,商家數據,分類數據,品牌數據
一個完整的數據,拆分成多個維度,每個維度獨立存儲,就避免了一個維度的數據變更就要全量更新所有數據的問題
不同維度的數據,因為數據量的不一樣,可以采取不同的存儲策略
系統拆分
系統拆分更加細:依賴服務、MQ、數據異構Worker、數據同步Worker、Redis、Nginx+Lua
每個部分的工作專注,影響少,適合團隊多人協作
異構Worker的原子數據,基於原子數據提供的服務更加靈活
聚合Worker將數據聚合后,減少redis讀取次數,提升性能
前端展示分離為商品詳情頁前端展示系統和商品介紹前端展示系統,不同特點,分離部署,不同邏輯,互相不影響
異步化
異步化,提升並發能力,流量削峰
消息異步化,讓各個系統解耦合,如果使用依賴服務調用商品詳情頁系統接口同步推送,那么就是耦合的
緩存數據更新異步化,數據異構Worker同步調用依賴服務接口,但是異步更新redis
動態化
數據獲取動態化:nginx+lua獲取商品詳情頁數據的時候,按照維度獲取,比如商品基本數據、其他數據(分類、商家)
模板渲染實時化:支持模板頁面隨時變化,因為采用的是每次從nginx+redis+ehcache緩存獲取數據,渲染到模板的方式,因此模板變更不用重新靜態化HTML
重啟應用秒級化:nginx+lua架構,重啟在秒級
需求上線快速化:使用nginx+lua架構開發商品詳情頁的業務邏輯,非常快速
多機房多活
Worker無狀態,同時部署在各自的機房時采取不同機房的配置,來讀取各自機房內部部署的數據集群(redis、mysql等)
將數據異構Worker和數據聚合Worker設計為無狀態化,可以任意水平擴展
Worker無狀態化,但是配置文件有狀態,不同的機房有一套自己的配置文件,只讀取自己機房的redis、ssdb、mysql等數據
每個機房配置全鏈路:接入nginx、商品詳情頁nginx+商品基本信息redis集群+其他信息redis集群、商品介紹nginx+商品介紹redis集群
部署統一的CDN以及LVS+KeepAlived負載均衡設備
隊列化
任務等待隊列
任務排重隊列(異構Worker對一個時間段內的變更消息做排重)
失敗任務隊列(失敗重試機制)
優先級隊列,刷數據隊列(依賴服務洗數據)、高優先級隊列(活動商品優先級高)
並發化
數據同步服務做並發化+合並,將多個變更消息合並在一起,調用依賴服務一次接口獲取多個數據,采用多線程並發調用
數據聚合服務做並發化,每次重新聚合數據的時候,對多個原子數據用多線程並發從redis查詢
redis:
本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。