小型電商網站的頁面展示采用頁面全量靜態化的思想。數據庫中存放了所有的商品信息,頁面靜態化系統,將數據填充進靜態模板中,形成靜態化頁面,推入 Nginx 服務器。用戶瀏覽網站頁面時,取用一個已經靜態化好的 html 頁面,直接返回回去,不涉及任何的業務邏輯處理。
下面是頁面模板的簡單 Demo 。
<html> <body> 商品名稱:#{productName}<br> 商品價格:#{productPrice}<br> 商品描述:#{productDesc} </body> </html>
這樣做,好處在于,用戶每次瀏覽一個頁面,不需要進行任何的跟數據庫的交互邏輯,也不需要執行任何的代碼,直接返回一個 html 頁面就可以了,速度和性能非常高。
對于小網站,頁面很少,很實用,非常簡單,JAVA 中可以使用 velocity、freemarker、thymeleaf 等等,然后做個 cms 頁面內容管理系統,模板變更的時候,點擊按鈕或者系統自動化重新進行全量渲染。
壞處在于,僅僅適用于一些小型的網站,比如頁面的規模在幾十到幾萬不等。對于一些大型的電商網站,億級數量的頁面,你說你每次頁面模板修改了,都需要將這么多頁面全量靜態化,靠譜嗎?每次渲染花個好幾天時間,那你整個網站就廢掉了。
大型電商網站的商品詳情頁系統架構
大型電商網站商品詳情頁的系統設計中,當商品數據發生變更時,會將變更消息壓入 MQ 消息隊列中。緩存服務從消息隊列中消費這條消息時,感知到有數據發生變更,便通過調用數據服務接口,獲取變更后的數據,然后將整合好的數據推送至 redis 中。Nginx 本地緩存的數據是有一定的時間期限的,比如說 10 分鐘,當數據過期之后,它就會從 redis 獲取到最新的緩存數據,并且緩存到自己本地。
用戶瀏覽網頁時,動態將 Nginx 本地數據渲染到本地 html 模板并返回給用戶。
雖然沒有直接返回 html 頁面那么快,但是因為數據在本地緩存,所以也很快,其實耗費的也就是動態渲染一個 html 頁面的性能。如果 html 模板發生了變更,不需要將所有的頁面重新靜態化,也不需要發送請求,沒有網絡請求的開銷,直接將數據渲染進最新的 html 頁面模板后響應即可。
在這種架構下,我們需要保證系統的高可用性。
如果系統訪問量很高,Nginx 本地緩存過期失效了,redis 中的緩存也被 LRU 算法給清理掉了,那么會有較高的訪問量,從緩存服務調用商品服務。但如果此時商品服務的接口發生故障,調用出現了延時,緩存服務全部的線程都被這個調用商品服務接口給耗盡了,每個線程去調用商品服務接口的時候,都會卡住很長時間,后面大量的請求過來都會卡在那兒,此時緩存服務沒有足夠的線程去調用其它一些服務的接口,從而導致整個大量的商品詳情頁無法正常顯示。
這其實就是一個商品接口服務故障導致緩存服務資源耗盡的現象。