簡介 Varnish是一款高性能、開源的緩存反向代理伺服器。它從客戶端接受請求,並嘗試從緩存中響應請求,如果無法從緩存中提供響應,Varnish 向後端伺服器發起請求,獲取響應,將響應存儲在緩存中,然後把響應發送給客戶端。如果Varnish能夠從Cache中響應一個請求,所消耗的時間是微秒級別的,這 ...
簡介
Varnish是一款高性能、開源的緩存反向代理伺服器。它從客戶端接受請求,並嘗試從緩存中響應請求,如果無法從緩存中提供響應,Varnish 向後端伺服器發起請求,獲取響應,將響應存儲在緩存中,然後把響應發送給客戶端。如果Varnish能夠從Cache中響應一個請求,所消耗的時間是微秒級別的,這個響應速度比直接從HTTP伺服器響應請求的速度要快兩個數量級,緩存命中率越高,網站的訪問速度就越快。
主要特性
- 緩存位置:可以使用記憶體也可以使用磁碟,如果要使用磁碟的話推薦SSD做RAID1;
- 日誌存儲:日誌也存儲在記憶體中,存儲策略:固定大小,迴圈使用;
- 支持虛擬記憶體的使用;
- 有精確的時間管理機制,即緩存的時間屬性控制;
- 狀態引擎架構:在不同的引擎上完成對不同的緩存和代理數據進行處理,可以通過特定的配置語言設計不同的控制語句,以決定數據在不同位置以不同方式緩存;
- 緩存管理:以二叉堆格式管理緩存數據,做到數據的及時清理。
系統架構
Varnish主要有兩個進程:Management進程與Child進程(也稱為Cache進程)。
- Management進程:主要對子進程進行管理,實現應用新的配置、編譯VCL、監控varnish、初始化varnish以及提供一個命令行介面等;Management進程會每隔幾秒鐘探測一下Child進程以判斷其是否正常運行,如果在指定的時長內未得到Child進程的回應,Management將會重啟此Child進程。
- Child進程:生成線程池,負責對用戶請求進行處理,並通過hash查找返回用戶結果。
Child進程包含多種類型的線程,常見的有:
-
- Accept線程:接收新的連接請求並響應;
- Worker線程:child進程會為每個會話啟動一個worker線程,因此在高併發的場景中可能會出現數百個worker線程甚至更多;
- Object Expiry線程:從緩存中清理過期內容;
- Commad line線程 : 管理介面;
- Storage/hashing線程:緩存存儲;
- Log/stats線程:日誌管理線程;
- Backend Communication線程:管理後端主機線程。
日誌
為了與系統的其它部分進行交互,Child進程使用可以通過文件系統介面進行訪問的共用記憶體日誌(shared memory log),因此如果某線程需要記錄信息,其僅需要持有一個鎖,而後向共用記憶體中的某記憶體區域寫入數據,再釋放持有的鎖即可;需要註意,為了減少競爭,每個worker線程都使用了日誌數據緩存。
共用記憶體日誌大小一般為90M,其分為兩部分,前一部分為計數器,後半部分為客戶端請求的數據。Varnish提供了多個不同的工具,如varnishlog、varnishncsa或varnishstat等來分析共用記憶體日誌中的信息並能夠以指定的方式進行顯示。
演算法
Varnish的Director支持的挑選方法中主要有round-robin(輪詢)和random(隨機)兩種。其中,round-robin類型沒有任何參數,只需要為其指定各後端主機即可,併在某後端主機故障時不再將其視作挑選對象;random方法隨機從可用後端主機中進行挑選,每一個後端主機都需要一個.weight參數指定其權重,同時還可以使用.retires參數來設定查找一個健康後端主機時的嘗試次數。
Varnish2.1.0後,random挑選方法又多了兩種變化形式client和hash。client類型的Director使用client.identity作為挑選因數,這意味著client.identity相同的請求都將被髮送至同一個後端主機;client.identity預設為cliet.ip,但也可以在VCL中將其修改為所需要的標識符。類似地,hash類型的Director使用hash數據作為挑選因數,這意味著對同一個URL的請求將被髮往同一個後端主機,其常用於多級緩存的場景中。無論是client還hash,當其傾向於使用後端主機不可用時將會重新挑選新的後端其機。
VCL工具
Varnish Configuration Language(VCL),Varnish配置緩存策略的工具,它是一種基於“域”(domain specific)的簡單編程語言,可以使用運算符包括“ =、==、!、&& ”等,支持使用正則表達式進行字元串匹配,允許用戶使用set自定義變數,支持if判斷語句,也有內置的函數和變數等。VCL策略在啟用前,會由management進程將其轉換為C代碼,而後再由gcc編譯器將C代碼編譯成二進位程式,編譯完成後management負責將其連接至varnish實例,即child進程。
後端存儲
Varnish支持多種不同類型的後端存儲,這可以在varnishd啟動時使用-s選項指定。後端存儲的類型包括:
- file:使用特定的文件存儲全部的緩存數據,並通過操作系統的mmap()系統調用,將整個緩存文件映射至記憶體區域(如果條件允許);
- malloc:使用malloc()庫調用在varnish啟動時向操作系統申請指定大小的記憶體空間以存儲緩存對象;
- persistent(experimental):與file的功能相同,但可以持久存儲數據(即重啟varnish數據時不會被清除),但仍處於測試階段。
Varnish無法追蹤某緩存對象是否存入了緩存文件,也就無從得知磁碟上的緩存文件是否可用,因此file存儲方法在varnish停止或重啟時會清除數據;而persistent方法的出現對此有了一個彌補,但persistent仍處於測試階段,其僅適用於有著巨大緩存空間的場景。
選擇使用合適的存儲方式有助於提升系統性。從經驗的角度來看,建議在記憶體空間足以存儲所有的緩存對象時使用malloc的方法,而file存儲有著更好的性能表現。需要註意的是,varnishd實際上使用的空間比使用-s選項指定的緩存空間更大,一般說來,其需要為每個緩存對象多使用差不多1K左右的存儲空間,這意味著,對於100萬個緩存對象的場景來說,其使用的緩存空間將超出指定大小1G左右。另外,為了保存數據結構等,varnish自身也會占去不小的記憶體空間。
為varnishd指定使用的緩存類型時,-s 選項可接受的參數格式如下:
- malloc [size] 或 file [path [size [granularity]]] 或 persistent path size {experimental}
VCL內置函數
- vcl_recv函數:用於接收和處理請求。當請求到達併成功接收後被調用,通過判斷請求的數據來決定如何處理請求。此函數一般以如下幾個關鍵字結束:
- pass:進入pass模式,把請求控制權交給vcl_pass函數;
- pipe:進入pipe模式,把請求控制權交給vcl_pipe函數;
- error code [reason]:返回code給客戶端並放棄處理該請求;code是錯誤標識,例如200、405等;reason是錯誤提示信息。
- vcl_pipe函數:此函數在進入pipe模式時被調用,用於將請求直接傳遞至後端主機,在請求和返回的內容沒有改變的情況下,將不變的內容返回給客戶端,直到這個鏈接關閉。此函數一般以如下幾個關鍵字結束:
- error code [reason]
- pipe
- vcl_pass函數:此函數在進入pass模式時被調用,用於將請求直接傳遞至後端主機,後端主機應答數據後送給客戶端,但不進行任何緩存,在當前連接下每次都返回最新的內容。此函數一般以如下幾個關鍵字結束:
- error code [reason]
- pass
- vcl_hash:表示在緩存里查找被請求的對象,並且根據查找的結果把控制權交給函數vcl_hit或者函數vcl_miss
- vcl_hit函數:在執行vcl_hash後,如果在緩存中找到請求的內容,將自動調用該函數。此函數一般以如下幾個關鍵字結束:
- deliver:表示將找到的內容發送給客戶端,並把控制權交給函數vcl_deliver
- error code [reason]
- pass
- vcl_miss函數:在執行val_hash後,如果沒有在緩存中找到請求的內容時自動調用該方法,此函數可以用於判斷是否需要從後端伺服器取內容。此函數一般以如下幾個關鍵字結束:
- fetch:表示從後端獲取請求的內容,並把控制權交給vcl_fetch函數
- error code [reason]
- pass
- vcl_fetch函數:在從後端主機更新緩存並且獲取內容後調用該方法,接著,通過判斷獲取的內容來決定是否將內容放入緩存,還是直接返回給客戶端。此函數一般以如下幾個關鍵字結束:
- error code [reason]
- pass
- deliver
- vcl_deliver函數:在緩存中找到請求的內容後,發送給客戶端前調用此方法。此函數一般以如下幾個關鍵字結束:
- error code [reason]
- deliver
- vcl_timeout函數:此函數在緩存內容到期前調用。一般以如下幾個關鍵字結束:
- discard:表示從緩存中清除該內容。
- fetch
- vcl_discard函數:在緩存內容到期後或緩存空間不夠時,自動調用該方法。此函數一般以如下幾個關鍵字結束:
- keep:表示將內容繼續保留在緩存中
- discard
以下是VCL處理流程圖,通過下圖可以更清楚Varnish的工作過程:
Varnish處理 HTTP請求 的過程分為以下幾個步驟:
- Receive狀態,也就是請求處理的入口狀態,根據VCL規則判斷該請求應該Pass或Pipe,或者進入Lookup(本地查詢);
- PIPE狀態,不可緩存數據,直接管道後端處理;
- Lookup狀態,進入此狀態後,會在hash表中查找數據,若找到,則進入Hit狀態,否則進入miss狀態;
- Pass狀態,在此狀態下,會進入後端請求,即進入Fetch狀態;
- Fetch狀態,在Fetch狀態下,對請求進行後端獲取,發送請求,獲得數據,併進行本地存儲;
- Deliver狀態,將獲取到的數據發送給客戶端,然後完成本次請求。
VCL內置公用變數
- 當請求到達後,可以使用的公用變數如下所示:
- req.backend:指定對應的後端主機
- server.ip:表示伺服器端IP
- client.ip:表示客戶端IP
- req.request:指定請求的類型,例如GET、HEAD、POST等
- req.url:指定請求的地址
- req.proto:表示客戶端發起請求的HTTP協議版本
- req.http.header:表示對應請求中的http頭部信息
- req.restarts:表示請求重啟的次數,預設最大值為4
- Varnish 在向後端主機請求時,可以使用的公用變數如下所示:
- beresp.request:指定請求的類型,例如GET、HEAD等
- beresp.url:指定請求的地址
- beresp.proto:表示客戶端發起請求的HTTP協議版本
- beresp.http.header:表示對應請求中的http頭部信息
- beresp.ttl:表示緩存的生存周期,也就是cache保留多長時間,單位是秒
- 從cache或者後端主機獲取內容後,可以使用的公用變數如下所示:
- obj.status:返回內容的請求狀態代碼,例如200、302、504等
- obj.cacheable:返回的內容是否可以緩存,也就是說,如果HTTP返回是200、203、300、301、302、404、410等,並且有非0的生存期,則可以緩存
- obj.valid:表示是否是有效的HTTP應答
- obj.response:返回內容的請求狀態信息
- obj.proto:返回內容的HTTP協議版本
- obj.ttl:返回內容的生存周期,也就是緩存時間,單位是秒
- obj.lastuse:返回上一次請求到現在的間隔時間,單位是秒
- 對客戶端應答時,可以使用的公用變數如下所示:
- resp.status:返回客戶端的HTTP狀態代碼
- resp.proto:返回客戶端的HTTP協議版本
- resp.http.header:返回客戶端的HTTP頭部信息
- resp.response:返回客戶端的HTTP狀態信息
如果想要瞭解更多請查閱Varnish官方文檔 查看文檔。