1. 程式人生 > >快取中介軟體-快取架構的實現(上)

快取中介軟體-快取架構的實現(上)

快取中介軟體-快取架構的實現(上)

前言

一眨眼,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快取,負載層快取。

如果存在什麼問題,或者疑惑,可以私信或@我