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