緩存中間件-緩存架構的實現(上)

緩存中間件-緩存架構的實現(上)

前言

一眨眼,2019年就過去了。我但願從按照中間件,分別闡述一些常見的架構問題,以及解決方案。一方面這些問題與解決方案具有必定通用性 。另外一方面,也算是面試中常見的問題。前端

我但願根據本身待過各類規模公司的經驗來談一些見解。nginx

  • 若是是針對大部分小公司的工做或面試,這些問題都稍微留下個印象便可。由於小公司的技術對這些問題並非很看重,或者說機會用不到(小型公司每每追求產品功能的實現,業務的推動等)。
  • 若是是針對大部分中型公司的工做或面試,但願能夠完整地知道這些問題與解決方案。由於在中型公司中,這些問題都或多或少遇到,甚至是須要迫切解決的。
  • 若是是大型公司的話,那麼不只僅須要知道這些問題與解決方案。還須要從中理解爲何會有這樣的問題,爲何這樣解決,在現有的項目中應該如何應用,是否提高空間等。由於在大公司中,一方面其內部每每採用自研框架,其它框架可以借鑑的只有方案,思想等精髓;另外一方面大公司不缺少那些應用開源框架的人,缺的是把握方案通用思想的人。

若是上述沒法理解的話,你們能夠從功能性追求與非功能性追求兩個方面去思考。就像寫一個簡單的方法同樣,最基本的要求是實現其功能,緊接着就是不斷追求其非功能性(如性能,擴展性,安全性等)。放大來看,對於公司的技術發展也是如此,或者說更爲嚴格。golang

以後找個機會,專門寫個博客,來談談我對公司技術與公司的見解。web

話題收回來,接下來,讓我開始有關中間件問題與解決方案的闡述吧。面試

概述

緩存的認識

既然說起緩存中間件相關的問題及方案,首先就要談談這個緩存。算法

本來我想經過高速緩存舉例,可是想了想仍是用內存舉例子吧。數據庫

好比咱們如今玩的單機遊戲,每每都容量都很是大(幾十G,乃至上百G),輕輕鬆鬆都超過了電腦內存(16G)。那麼很明顯電腦在運行遊戲時,是不可能將整個遊戲文件都放入內存的。可是若是文件都在硬盤裏,須要的時候再讀取,顯然硬盤的讀寫速度時不夠的(因爲遊戲文件類別不少,因此硬盤不可能一直順序讀寫),那遊戲也會常常卡頓,加載緩慢等。那麼該如何解決這個問題呢?後端

其實這個問題和咱們業務中遇到的一些問題是很相似的。一方面咱們但願用戶能夠在保證用戶體驗的前提下查詢數據(如設備列表,訂單列表等),另外一方面咱們不可能將全部數據都放在內存(內存的讀寫速度比硬盤快,因此就不解釋爲何用硬盤了)中。那麼到底該怎麼解決這個問題呢?瀏覽器

這裏就須要說到局部性原理了。局部性原理指的是數據的訪問每每趨向於彙集在較小的連續區域。這裏的連續區域包含兩個方面:緩存

  • 時間維度:一個被使用的數據,在接下來較短的時間內,每每會被再次使用。
  • 空間維度:一個被使用的數據,其關聯的數據,每每也會被使用。

局部性原理是在內存,高速緩存部分,提出來用於解決問題的。

其實,我與朋友交流分佈式的一些想法時,常常說:分佈式系統和單機內部是很是類似的,不少理念都是相通的。當想通了這點後,就能夠去思考二者的區別的。

緩存中間件其實就是利用了局部性原理,不過緩存中間件自己只實現了局部性原理的時間維度。這也是爲何不少人都說緩存中間件是用來保存熱點數據,符合二八定律。不過咱們能夠在應用部分實現局部性原理的空間維度。

緩存的定位

五六年前,有人就提出一個有關緩存的問題,那就是緩存做爲一個非持久化數據,咱們該怎麼劃分它。是否須要保證它的可用性。其中就有一位阿里的前輩在他的書中提到,他更傾向於認爲緩存並非一種持久化數據,不應將緩存做爲一種可靠數據源。可是這位前輩也表示現有的框架中對緩存依賴較重,應該在必定程度上保護它們,避免緩存雪崩等狀況。

個人見解是,在現有的技術體系中,緩存中間件等已經再也不只是一個緩存了。一方面咱們已經將Session等重要數據放在了緩存中,而且目前沒有一個更合適的對應存儲(我認爲暫時也不須要一個新的存儲方式。可是若是須要的話,能夠將緩存中間件實例等按照內容的生命週期等進行分組)。另外一方面,咱們會須要明確緩存在系統中職責,它只是用來做爲緩存,以及一些分佈式內存。可是諸如單機全部的內部調用,應該經過消息中間件或RPC等來實現。而且明確不一樣緩存的職責,如Session不應放在Cookie中等。

緩存的分類

緩存框架大體能夠從客戶端到數據源,分紅如下分類。

  • 瀏覽器緩存
    • Cookie
    • LocalStorage
    • SessionStorage
  • CDN緩存
  • 負載層緩存
    • Nginx緩存模塊
    • Squid緩存服務器
    • Lua擴展
  • 應用層緩存
    • Etag
    • ThreadLocal
    • Guava
  • 外部緩存
    • Redis
  • 數據庫緩存
    • MySql緩存

我特地查詢了一下百度,首頁上的有關緩存架構的博客,一半都只是在圍繞着緩存中間件闡述緩存架構,剩下的通常也每每在大分類上有所遺漏(如瀏覽器緩存,數據庫緩存)。固然也有一些博客在專門的領域闡述得較爲深刻,或者層次的劃分比較不錯。故本博客只是在闡述現階段我對緩存架構的認識(也借鑑了一些書籍,課程的緩存體系)。

瀏覽器緩存

瀏覽器緩存,也是不少時候被後端所遺忘的部分。由於這已經不屬於後端的工做了,但這必定屬於架構師或者相關技術負責的職責。固然還有一個緣由是我作過專門的前端開發。

說白了,就是在瀏覽器保存一部分數據,固然這須要前端進行開發。

這裏直接上圖,你們能夠看一下Cookie,LocalStorage,SessionStorage:

PS:圖片來自網絡

優點

因爲是瀏覽器緩存,位於整個web請求相應框架的client端,因此對業務提供方沒有任何負載壓力與影響。只是客戶端的瀏覽器存在些許的存儲佔據與計算負載。

注意

  • Cookie等的存儲容量是有限的,須要注意分配。
  • Cookie等的存儲是明文的,不能夠存儲敏感數據,不然會存在安全隱患。
  • Cookie等須要注意存儲時間時間的有效設置。
  • Cookie等存在必定的學習成本,與相關特性(如Cookie的域名設置問題,父域名沒法讀子域名的Cookie數據)。
  • Cookie等須要明確業務中有哪些數據適合放在這裏,如域名等。

實際應用

在我以前負責的IOT項目中,頁面每每存在大量的數據,如終端列表,傳感器列表,監測點列表等。而且數據間存在必定數據關係,如須要經過現存的終端列表來獲取對應傳感器列表,又如經過傳感器列表來獲取對應報警列表等。

爲了不頁面切換時,爲了獲取一個列表而須要屢次請求(如爲了得到已選定的終端列表的傳感器列表,須要先請求終端列表),因此經過LocalStorage來存儲終端列表。

CDN緩存

CDN,Content Delivery Network,即內容分發網絡。

  • CDN是構建網絡上的內容分發網絡
  • CDN可使得用戶就近獲取所需內容,避免網絡擁塞,提升用戶訪問速度
  • CDN依靠部署在各地的服務器,經過鏡像服務器實現內容同步,其包括負載均衡,內容分發,調度等模塊。

優點

  • 下降訪問延遲。使得用戶就近獲取所需內容,避免過多路由形成用戶訪問延遲問題。
  • 下降服務器壓力。畢竟放在CDN服務器的內容,就不用到應用服務器獲取了。
  • 消除運營商差異。消除運營商之家互聯的瓶頸形成的影響,使得全部用戶得到一樣的訪問質量
  • 集羣抗攻擊。普遍分佈的CDN節點,可有有效避免DDOS等攻擊。

缺點

  • 同步緩慢。因爲CDN是大量且分層的節點分佈,因此數據的下發與同步會比較緩慢。
  • 若是是使用收費服務,則須要必定支出。若是是自建CDN,則須要技術付出。我的推薦,沒必要要的話,仍是直接採用CDN收費服務吧,性價比更高一些。
  • 自身Web體系須要進行相應的調整。如CDN文件更新與服務器文件更新(版本號等手段)等問題。

關鍵技術

該部份內容,引自網易雲課堂。

  • 緩存
    • 緩存代理軟件:Squid
    • 緩存算法決定命中率,源服務器壓力,FTP節點存儲能力
  • 分發能力
    • 分發能力取決於IDC(網絡數據中西)能力和IDC策略性分佈
  • 負載均衡
    • 負載均衡軟件:Nginx
    • 負載均衡(智能調度)決定最佳路由,響應時間,可用性,服務質量
  • 基於DNS
    • DNS服務器軟件:BIND
    • 基於DNS的負載均衡以CNAME實現域名中專,智取最優節點服務
    • 緩存點有客戶端瀏覽器緩存,本地DNS服務器緩存
    • 緩存內哦讓那個有DNS地址緩存,客戶請求內容緩存,動態內容緩存
  • 支持協議
    • 靜動態加速(圖片加速,https帶證書加速)
    • 下載加速
    • 流媒體加速
    • 企業應用加速
    • 手機應用加速

就當擴展一下見識吧(囧)

實際應用

若是寫過前端代碼,會知道有的時候,咱們採用的jQuery等通用JS,CSS等大可能是使用公共的cdn地址。

有的公司,會將公司的一些公共JS,圖片等靜態資源(尤爲是公司Logo等),放在CDN上。進行網頁開發時,直接引用對應的CDN地址。

負載層緩存

負載層緩存通常是與負載均衡器相關的緩存,這裏我就拿Nginx舉例。

Nginx能夠經過如下三種手段,實現緩存:

  • 自己的緩存模塊
  • 轉發請求至對應緩存服務器
  • 能夠經過lua模塊,直接從外部緩存(如Redis等)獲取緩存數據

接下來一一闡述

Nginx緩存模塊

Nginx的http_proxy模塊,能夠實現相似於Squid的緩存功能.

Nginx對客戶端已經訪問的內容在Nginx服務器本地創建緩存副本,那麼在必定時間內再次訪問這些內容時,就不須要請求後面的應用服務器了。

與此同時,當後面的應用服務器沒法提供服務時(如宕機),Nginx服務器上的緩存資源還可以迴應相關的用戶請求,提升了後面應用服務器的魯棒性(健壯性)。

優點

  • 商業成本無。Nginx是開源的,無需商業付費。
  • 技術迭代成本低。現有的Web體系大多采用Nginx,進行技術迭代時,在Nginx只須要增長一個新的模塊便可。
  • 可定製。能夠根據須要,對指定路徑,指定資源等進行定製化的緩存策略。

缺點

  • 須要對Nginx的緩存模塊進行必定的認識與學習。畢竟不少人使用Nginx都只是CV一下配置。
  • 須要根據業務須要與技術特色,進行緩存策略的調整。若是缺少經驗與足夠的認識,可能會指定出不恰當的緩存技術規範(如哪些數據該走Nginx緩存模塊等)。

基本認識

緩存文件位置設置

經過proxy_cache_path參數指定。proxy_cache_path有兩個必填參數:

  • 第一個參數爲緩存目錄。
  • 第二個keys_zone參數指定緩存名稱和佔用內存空間的大小。
指定特定請求被緩存
  • Nginx默認會緩存全部get和head方法的請求結果,緩存的key默認使用請求字符串
  • 自定義key。如proxy_cache_key "$host$request_uri$cookie_user";
  • 指定請求至少被髮送了多少次以上才被緩存,從而避免低頻請求被緩存。如proxy_cache_min_uses 5;
  • 指定哪些方法的請求被緩存。如proxy_cache_methods GET HEAD POST;
緩存有效期

默認狀況下,緩存內容是長期留存,除非緩存的容量超出誰知的限制。也能夠自定義設置有效時間。如:

  • 響應狀態碼爲200 302時,10分鐘有效期限:proxy_cache_valid 200 302 10m;
  • 對任何狀態碼,5分鐘有效期限:proxy_cache_valid any 5m;
部分請求跳過緩存

經過proxy_cache_bypass指令,明確請求對應的響應來自原始數據,而不是緩存。

例如(該示例來自網易雲課堂) proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment;

表示:若是任何一個參數不爲空,或者不等於0,nginx就不會查找緩存,直接進行代理轉發。

擴展

網頁的緩存是由HTTP消息頭中的「Cache-control」來控制的,常見的取值有private,no-cache,max-age,must-revalidate等,默認爲private。詳見下表:

在這裏插入圖片描述

Squid緩存服務器

其實Squid緩存服務器與Nginx緩存十分相似(畢竟Nginx的緩存就是仿照Squid的),因此這裏只是表示有這麼個選擇,不作深刻。

Lua擴展

Nginx是C語言開發(這也是Nginx高性能的根本緣由之一),而且Nginx模塊須要用C開發,而且須要符合一系列複雜的規則,還須要熟悉Nginx源碼。

ngx_lua模塊

因此Nginx提供了ngx_lua模塊,經過lua解釋器集成進Nginx。而ngx_lua模塊具有如下特性:

  • 高併發,非阻塞地處理各類請求。
  • Lua內建協程(可對比golang),從而將異步回調轉換成順序調用的形式。
  • 每一個協程都有一個獨立的全局環境(變量空間),繼承於全局共享的,只讀的「comman data」。

上述只是簡單提一下Lua擴展,感興趣的能夠查詢相關資料。

這裏繼續闡述Lua擴展,實現緩存功能。

實際應用

爲了幫助你們理解,先說一下實際應用。

Nginx針對HTTP請求處理,有十一個階段。與之相對的,ngx_lua模塊的執行指令都包含在了上述的十一個階段。這裏只說一下其中的content_by_lua指令,針對的是Nginx的content階段,能夠在location,location if範圍內使用,主要做爲內容處理器,接收請求處理並輸出響應。

具體配置以下:

在這裏插入圖片描述

這樣配置後,直接瀏覽器訪問本地ip(或者經過curl命令),能夠看到「Hello,world」。

固然,這種用法相對比較初級。在OpenResty中存在一些組件,能夠幫助ngx_lua模塊直接訪問Redis這樣的數據源。這樣就能夠將一些簡單的數據經過這種方式來進行訪問,下降應用服務器壓力。

優點

  • 下降應用服務器壓力
  • 門檻較低。能夠按照一些配置模板,直接進行使用
  • 擴展性較強。ngx_lua模塊的應用上限仍是比較高的
  • 靈活性強。ngx_lua模塊的靈活性,表示其在緩存方面具備較高的靈活性

缺點

  • 精通難。想要精通這部分的話,須要瞭解lua腳本,以及Nginx的HTTP請求階段等。
  • 額外的開發任務。除了應用開發外,還須要專門的lua開發。
  • 耦合性較高。一個頁面,一個功能,卻每每須要進行Nginx與後端聯合開發。
  • 任務難以界定。在業務上難以界定一些功能的開發該歸於哪一個模塊(Nginx,後端)。

總結

至此,咱們已經瞭解了緩存架構中最靠近用戶的三層緩存:瀏覽器緩存,CDN緩存,負載層緩存。

若是存在什麼問題,或者疑惑,能夠私信或@我。

相關文章
相關標籤/搜索