快取中介軟體-快取架構的實現(上)
快取中介軟體-快取架構的實現(上)
前言
一眨眼,2019年就過去了。我希望從按照中介軟體,分別闡述一些常見的架構問題,以及解決方案。一方面這些問題與解決方案具備一定通用性 。另一方面,也算是面試中常見的問題。
我希望根據自己待過各種規模公司的經驗來談一些看法。
- 如果是針對大部分小公司的工作或面試,這些問題都稍微留下個印象即可。因為小公司的技術對這些問題並不是很看重,或者說機會用不到(小型公司往往追求產品功能的實現,業務的推進等)。
- 如果是針對大部分中型公司的工作或面試,希望可以完整地知道這些問題與解決方案。因為在中型公司中,這些問題都或多或少遇到,甚至是需要迫切解決的。
- 如果是大型公司的話,那麼不僅僅需要知道這些問題與解決方案。還需要從中理解為什麼會有這樣的問題,為什麼這樣解決,在現有的專案中應該如何應用,是否提升空間等。因為在大公司中,一方面其內部往往採用自研框架,其它框架能夠借鑑的只有方案,思想等精髓;另一方面大公司不缺乏那些應用開源框架的人,缺的是把握方案通用思想的人。
如果上述無法理解的話,大家可以從功能性追求與非功能性追求兩個方面去思考。就像寫一個簡單的方法一樣,最基本的要求是實現其功能,緊接著就是不斷追求其非功能性(如效能,擴充套件性,安全性等)。放大來看,對於公司的技術發展也是如此,或者說更為嚴格。
之後找個機會,專門寫個部落格,來談談我對公司技術與公司的看法。
話題收回來,接下來,讓我開始有關中介軟體問題與解決方案的闡述吧。
概述
快取的認識
既然提及快取中介軟體相關的問題及方案,首先就要談談這個快取。
原本我想通過快取記憶體舉例,但是想了想還是用記憶體舉例子吧。
比如我們現在玩的單機遊戲,往往都容量都非常大(幾十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快取,負載層快取。
如果存在什麼問題,或者疑惑,可以私信或@我