大型電商網站的頁面靜態化方案是如何支撐億級流量的?

  • 2019 年 11 月 21 日
  • 筆記

前言

我們小夥伴們在訪問淘寶、網易等大型網站時有沒有考慮到,網站首頁、商品詳情頁以及新聞詳情頁面是如何處理的?怎麼能夠支撐這麼大流量的訪問呢?

很多小夥伴們就會提出他們都採用了靜態化的方案,這樣用戶請求直接獲取靜態數據html,就不需要訪問數據庫了,性能就會大大提高;而且提高網站SEO優化。

那今天就帶着大家聊一下靜態化。把之前工作場景中靜態化方案遇到的問題,以及如何演變的,分享給小夥伴。

方案一:網頁靜態HTML化

這個方案是最早使用的方案,我們就拿CMS系統舉例,類似網易的新聞網站

核心流程圖:

上圖的核心思想:

  1. 管理後台調用新聞服務創建文章成功後,發送消息到消息隊列
  2. 靜態服務監聽消息,把文章靜態化,也就是生成html文件
  3. 在靜態服務器上面安裝一個文件同步工具,此工具的功能可以做到只同步有變動的文件,即做增量同步
  4. 通過同步工具把html文件同步到所有的web服務器上面

這樣的話就達到了,用戶訪問一些變化不大的頁面時,是直接訪問的html文件,直接在web服務器那邊直接返回,不需要在訪問數據庫了,系統吞吐量比較高。

這個方案的問題:

1、網頁布局樣式僵化,無法修改

如果產品經理覺得新聞詳情頁面的布局要調整一下,現在的不夠美觀,或者加個其他模塊,那就坑爹了 我們需要把所有的已經靜態html化的文章全部重新靜態化。這個是不現實的,因為像網易這麼大的體量,新聞量是很大的,會被搞死。

2、頁面會出現暫時間不一致

會出現用戶剛剛再看最新的新聞,刷新一下又不存在了。這個是因為同步工具在同步到web服務器是要有時間的,同步到web服務器A上面了,但web服務器B還沒有來得及同步。 用戶在訪問的時候通過nginx進行負載均衡,隨機把請求分配給web服務器的導致的。當然可以調整nginx負載均衡策略去解決。

3、Html文件太多,無法維護

這個是很明顯的問題,html文件會越來越多,對存儲空間要求很大,而且每台web服務器都一樣,浪費磁盤空間;將來遷移維護也會帶來很大的麻煩。

4、同步工具的不穩定

因為文件一旦多之後,同步工具穩定性就出現了問題

這個方案應該是比較傳統的(不推薦)

方案二:偽靜態化

什麼是偽靜態?

舉個例子:我們一般訪問一個文章,一般的鏈接地址為:

http://www.xxx.com/news?id=1

代表請求id為1的文章。不過這種鏈接方式對SEO不是太友好(SEO對網站來說太重要了)

所以一般進行改造:http://www.xxx.com/news/1.html 這樣看上去就是個靜態頁面。

一般我們可以採用nginx對url進行rewrite。小夥伴如何有興趣可以自行了解,比較簡單。之所以是偽靜態其實也是需要動態處理的。

針對方案一上面問題,方案進一步的演化,如下圖:

此方案的核心思想

  1. 管理後台調用新聞服務創建文章成功後,發送消息到消息隊列
  2. 緩存服務監聽消息,把文章內容緩存到緩存服務器上面
  3. 用戶發起請求,web服務器根據id,直接查詢緩存服務器
  4. 獲取數據返回給用戶

此方案就解決了方案一的一個大問題,就是html文件多的問題,因為不需要生成html,而且用緩存的方式,解決不需要訪問數據庫,提升系統吞吐量。

不過此方案的問題:

  1. 網頁布局樣式維護成本比較高,因為此方案照樣是把所有的內容放到了緩存中,如果需要修改布局,需要重新設置緩存。
  2. 分佈式緩存壓力比較大,一旦緩存故障就導致所有請求會查詢數據庫,導致系統崩潰

還有個小問題,就是實時數據處理,就是頁面中如價格,庫存需要到後台讀取的。

當然小夥伴也許就會說,也可以處理啊,用戶把商品內容請求到後,然後在用瀏覽器發送異步的ajax請求獲得商品數量就好了啊。這樣就是無形的增加了一次請求。(此問題可以忽略)

此方案類似很多公司都在使用,如:同程旅遊等

方案三:布局樣式模板化

針對方案二的問題,我們可以採用openresty技術方案進行,利用http模板插件lua腳本進行解決

如下圖:

這裡說明一下上圖中我們小夥伴不需要全部都要了解,這個是比較全的商品詳情頁的解決方案,涉及到了三級緩存這個概念

我們主要看的是上面怎麼會有兩層ngnix,分發層和應用層,這個是什麼意思?

應用層nginx

應用層nginx是什麼意思?nginx一般被用做負載均衡,其實nginx還有很多的功能,尤其他的openresty擴展 + lua腳本語言結合起來可以完成很多功能

小夥伴可以理解為lua腳本語言就是類似java語言,可以動態處理業務,如:本地緩存處理,遠程http訪問,訪問redis等。

應用層nginx就是利用了http模板 + 緩存通過lua腳本完成的網頁渲染

http模板

  1. 應用層nginx通過lua腳本語言先獲取本地商品數據,然後和http模板進行渲染,形成最終商品詳情頁返回給用戶
  2. 如果應用層nginx本地的緩存沒有此商品數據,就通過lua腳本發起http請求訪問web服務器,獲取商品數據。
  3. web服務器會向redis或本機的ehcache請求商品數據(這裡涉及三級緩存概念),如果存在此商品數據,直接返回給用戶;如果不存在則請求微服務訪問數據庫

這個思路就是通過http模板,解決了方案二中的布局樣式的問題,如果需要調整布局,只要改一下模板就行了,非常方便。也解決了實時性問題。

這邊涉及到的nginx本地緩存其實就是為了保證不需要訪問數據庫,提升系統吞吐量。小夥伴只要了解一下思路,如果不了解openresty和lua可以自行上網了解

分發層ngnix

為什麼上面還有一層分發層呢?這個是因為大型網站的商品數太多了,應用層nginx的本地緩存是有限的,不可能把所有的商品數據緩存在同一個服務器的本地緩存

一台應用層nginx只能緩存部分商品數據,說到這裡小夥伴是不是應該就知道為什麼了吧?就是利用hash一致性算法,根據商品id路由分發到同一個應用層ngnix服務器。

分發層ngnix的作用就是hash策略的負載均衡,保證了商品id路由到固定的應用層服務器。

三級緩存保證了系統的穩定性,即使redis緩存崩潰,還有其他2個緩存保障。

總結:

  1. 方案三是比較完整的方案,很多大廠都在使用,能夠承受億級流量,但系統比較複雜。
  2. 如果對實時性要求不高,布局樣式調整不頻繁,可以考慮方案二,系統比較簡單

作者:老顧聊技術 來源:https://www.toutiao.com/i6671093883025228301