全站緩存時代

原則:動靜分離,分級緩存,主動失效。html

Web 開發中,接口會被分爲如下幾類:前端

  1. 純靜態頁面。打死我都不會修改的頁面。很長一段時間內,基本上不會修改。好比:關於咱們。git

  2. 純動態頁面。實時性,個性化要求比較高。頁面變化很大,或者每一個用戶看到的都不同,好比:朋友圈。github

  3. 短時靜態頁面。在必定時間內基本不會變化,或者是容忍不須要實時更新。好比:文章、新聞。redis

  4. 動靜結合頁面。這個頁面既有動態,也有靜態內容。也是實際應用中最多的。後端

對於以上類型的頁面,能夠作不一樣的緩存方案。各位大神們應該根據本身業務的狀況,靈活調整緩存方案。如下內容能夠做爲參考。api

模板渲染

高速發展的模板引擎,給前端渲染帶來了活力。Mustache、jade、hbs 靈活的模板語法讓頁面開發變得更省力和高效。瀏覽器

HtmlDOM == VeiwEngine.render(template ,data);緩存

瀏覽器只認識 DOM 結構的字符串,也就是常說的 HTML5 格式。對於前端來渲染 DOM,仍是後端渲染的問題,在此不用討論,爲了狀況前端的性能和體驗,後端渲染會更合適。對於同一個頁面,每次請求都會產生一次渲染嗎?渲染老是要計算的,這樣多浪費服務器性能啊!確實是這樣,除非你用了緩存。服務器

頁面緩存的方案

1. 純靜態頁面

直接放 CDN。純靜態頁面的訪問量通常不會很大,程序直接響應也是能夠的。

2. 純動態頁面

都說是動態頁面了,那就不要作頁面緩存了。能夠考慮作數據緩存,或者是 redis、DB 緩存。

3. 短時靜態頁面

1. 服務器端文件緩存

請求-->處理接口--> 模板渲染 ---> 存儲文件---> 響應文件

緩存動態頁面,你也能夠把生成的文件存到 CDN,而後讓 CDN 去響應請求。若是你的請求須要過一些驗證,那就把文件存儲到服務器,由業務服務器去響應請求。文件還有一個好處是:流。例如:FileReadStream.pipe(ResponseStream)。響應的時候,不須要把文件的內容加載到內存,而是直接用 stream 的方式響應。可是弊端也很多,文件存儲,會有併發讀寫死鎖問題。

還有一個問題,分佈式系統。可能你有 A、B、C 三個服務器。A 服務器生成了一個文件,還須要實時同步到 B 和 C。固然也可讓 A、B、C 掛載同一個磁盤。問題又來了,這個文件要不要備份呢?

2. Redis Cache

請求--> 接口接口---> 模板渲染 --> 存儲數據--> 響應 DOM

把請求的 url 當作 key,把模板渲染好的數據當作值,而後根據緩存規則,把數據存儲到 redis。

這種小成本的緩存在咱們的系統中有實踐,的確大幅提升了系統的響應時間和 QPS,頁面的請求大部分是從 redis 讀數據,而後返回,單機測試過極限性能,14k QPS。簡單描述一下。咱們稱之爲靜態化staticize

  1. 開始請求

  2. 請求校驗,filter 等等

  3. 查詢緩存 redis

  4. 若是有緩存,則直接響應

  5. 沒有緩存,查詢數據,從新渲染,存儲到 redis.

  6. 響應

  7. 若是需更新緩存,只須要刪掉對應的redis 值

4. 動靜結合的頁面

這種頁面在實際狀況中更常見。原則:靜態頁面緩存,動態部分異步請求。

動靜結合

靜態部分也是模板渲染過來的,瀏覽器會從 CDN 或者後臺緩存中獲取到靜態頁面。頁面響應的時間和瀏覽器的渲染會直接影響用戶體驗。動態更新的部分通常會在一些細節部分,好比頁面的登陸狀態。對於全部用戶來講,我看到的這個頁面,只有用戶頭像部分會不一致。若是系統爲每一個用戶生成一個靜態頁面成本就過高了,並且徹底不必。

這個頁面就變成了:頁面 == 短時靜態頁面 + 局部動態頁面。

『用戶狀態信息』這個特殊的動態內容,還須要用到本地的緩存機制。用戶在切換頁面的時候,每一個頁面都須要動態加載用戶信息,因此咱們的作法是在第一次請求到這個信息的時候,存儲到 localStorage,而後設置過時時間。退出的時候,主動清理 localStorage。

好比:個性化,我的推薦這種因人而異的板塊均可以作成局部動態頁面的形式。

5. 數據緩存

以上的方案一樣適用於異步請求。

對於CDN 或者其餘緩存來講,緩存不知道你存的內容是 DOM 仍是 JSON,仍是其餘格式。它只是幫你存儲數據。你一樣能夠的把,數據接口、局部 DOM 結構(非完整 html 格式)存儲到 CDN 或者是 redis 中。好比:頁面的配置信息,或者從相關推薦系統請求的 dom 結構。

緩存更新

通常會有主動失效和自動失效緩存機制。

CDN 和 redis 等緩存均可以根據規則設置緩存時間。緩存過時後,會再次獲取新的數據。
主動更新通常會用 API 調用方式實現。好比刪除 key,或者調用 CDN 接口進行刪除操做

clipboard.png

緩存穿透

通常會在第一次請求的時候生成緩存,若是服務器端沒有緩存,而後在同一時刻出現高併發請求,請求會直接到達業務邏輯部分,極可能致使系統直接掛掉。

解決辦法:

  1. 主動建立緩存。緩存求由系統定時建立。

  2. 請求的時候設置標誌位。第一個請求到達,標識這個 url 正在建立緩存,其餘請求進入等待隊列。

全站 CDN 加速

CDN 動態加速以下圖所示:

clipboard.png

例如個人網站有如下接口和頁面:

  1. http://www.localhost.com/ // 短時緩存,動靜結合

  2. http://www.localhost.com/api/user/1 // 純動態

  3. http://www.localhost.com/post/hello-world // 永久靜態

因此,一、3頁面會放到 CDN,2 直接去源站請求。怎麼作到呢?

  1. 在 CDN 配置自主源站。意味着請求 CDN 地址的時候,CDN 會去源站請求數據,而後緩存到 CDN 節點。

  2. 設置緩存規則

    / 緩存 1 分鐘
    /post/* 緩存 1 年
    /api/ 不設置緩存
  3. cname www.localhost.com 到 CDN 提供的空間域名

clipboard.png

多平臺 Mulit Origin

一個 URL 可能會在不一樣的平臺有不一樣的返回和表現形式。

產品的想法都是很完美,一個按鈕在不一樣的平臺會有不一樣的顯示狀態。實際狀況很是複雜,在咱們的系統中,出現過一個頁面出如今 七 個平臺,每一個平臺的顯示效果會不一致。不論是模板渲染,或者是 js 處理按鈕狀態等等都是很是複雜的,或者 pc 和移動端頁面表現出樣式和結構差別。若是還要把這個頁面放到緩存,就更加複雜了。

爲每一個平臺生成一份緩存?能夠!

平臺的識別來自 UserAgent,不一樣的瀏覽器或者 app,都有不一樣的UserAgent。不一樣的來源咱們稱之爲 Origin。Origin + url 就能夠生成惟一的 key,去識別惟一的緩存。緩存不限於 redis 和 文件緩存。

CDN 識別來源去讀取不一樣的文件,就須要 CDN 那邊作一些開發工做了。Upyun、七牛這邊暫時不支持的。BAT這種大公司他們本身維護的 CDN 就能完美地作到。

另外一種思路:

1個項目,兩個域名,2個動態 CDN。PC 和移動端頁面分離、接口共享。

例如:爲同一個項目配置兩個域名:www.localhost.comm.www.localhost.com,同時爲這兩個域名各設置一個動態 CDN。

由一項目提供兩個域名服務,好比:IndexController.main 處理請求 /homepage,移動端和 PC 端的請求路徑分別爲

  • http://m.www.localhost.com/homepage

  • http://www.localhost.com/homepage

main action 會根據請求來源url,分別渲染不一樣的頁面。不一樣的域名頁面,也就被不一樣的動態 CDN 緩存起來。

對於 /api/xxxx的接口,天然不須要作 PC 和移動端或者其餘平臺的區分,一個 action 就能夠解決了。這樣就避免了維護兩套系統的問題。

結語

以上,全站緩存基本完成。

不要憑空去拉高 QPS或者亂用緩存,根據你的業務和實際狀況來對待。最重要的事情就是要牢記:保持簡潔,按需使用。

參考文獻

相關文章
相關標籤/搜索