開(kāi)源HTTP加速器Varnish

2017年3月6日23:53:35 發(fā)表評(píng)論 3,635 ℃

一、關(guān)于Varnish

1、varnish系統(tǒng)架構(gòu)

varnish主要運(yùn)行兩個(gè)進(jìn)程:Management進(jìn)程和Child進(jìn)程(也叫Cache進(jìn)程)。

Management進(jìn)程主要實(shí)現(xiàn)應(yīng)用新的配置、編譯VCL、監(jiān)控varnish、初始化varnish以及提供一個(gè)命令行接口等。Management進(jìn)程會(huì)每隔幾秒鐘探測(cè)一下Child進(jìn)程以判斷其是否正常運(yùn)行,如果在指定的時(shí)長(zhǎng)內(nèi)未得到Child進(jìn)程的回應(yīng),Management將會(huì)重啟此Child進(jìn)程。

Child進(jìn)程包含多種類型的線程,常見(jiàn)的如:

Acceptor線程:接收新的連接請(qǐng)求并響應(yīng);

Worker線程:child進(jìn)程會(huì)為每個(gè)會(huì)話啟動(dòng)一個(gè)worker線程,因此,在高并發(fā)的場(chǎng)景中可能會(huì)出現(xiàn)數(shù)百個(gè)worker線程甚至更多;

Expiry線程:從緩存中清理過(guò)期內(nèi)容;

Varnish依賴“工作區(qū)(workspace)”以降低線程在申請(qǐng)或修改內(nèi)存時(shí)出現(xiàn)競(jìng)爭(zhēng)的可能性。在varnish內(nèi)部有多種不同的工作區(qū),其中最關(guān)鍵的當(dāng)屬用于管理會(huì)話數(shù)據(jù)的session工作區(qū)。

2、varnish日志

為了與系統(tǒng)的其它部分進(jìn)行交互,Child進(jìn)程使用了可以通過(guò)文件系統(tǒng)接口進(jìn)行訪問(wèn)的共享內(nèi)存日志(shared memory log),因此,如果某線程需要記錄信息,其僅需要持有一個(gè)鎖,而后向共享內(nèi)存中的某內(nèi)存區(qū)域?qū)懭霐?shù)據(jù),再釋放持有的鎖即可。而為了減少競(jìng)爭(zhēng),每個(gè)worker線程都使用了日志數(shù)據(jù)緩存。

共享內(nèi)存日志大小一般為90M,其分為兩部分,前一部分為計(jì)數(shù)器,后半部分為客戶端請(qǐng)求的數(shù)據(jù)。varnish提供了多個(gè)不同的工具如varnishlog、varnishncsa或varnishstat等來(lái)分析共享內(nèi)存日志中的信息并能夠以指定的方式進(jìn)行顯示。

3、VCL

Varnish Configuration Language (VCL)是varnish配置緩存策略的工具,它是一種基于“域”(domain specific)的簡(jiǎn)單編程語(yǔ)言,它支持有限的算術(shù)運(yùn)算和邏輯運(yùn)算操作、允許使用正則表達(dá)式進(jìn)行字符串匹配、允許用戶使用set自定義變量、支持if判斷語(yǔ)句,也有內(nèi)置的函數(shù)和變量等。使用VCL編寫(xiě)的緩存策略通常保存至.vcl文件中,其需要編譯成二進(jìn)制的格式后才能由varnish調(diào)用。事實(shí)上,整個(gè)緩存策略就是由幾個(gè)特定的子例程如vcl_recv、vcl_fetch等組成,它們分別在不同的位置(或時(shí)間)執(zhí)行,如果沒(méi)有事先為某個(gè)位置自定義子例程,varnish將會(huì)執(zhí)行默認(rèn)的定義。

VCL策略在啟用前,會(huì)由management進(jìn)程將其轉(zhuǎn)換為C代碼,而后再由gcc編譯器將C代碼編譯成二進(jìn)制程序。編譯完成后,management負(fù)責(zé)將其連接至varnish實(shí)例,即child進(jìn)程。正是由于編譯工作在child進(jìn)程之外完成,它避免了裝載錯(cuò)誤格式VCL的風(fēng)險(xiǎn)。因此,varnish修改配置的開(kāi)銷(xiāo)非常小,其可以同時(shí)保有幾份尚在引用的舊版本配置,也能夠讓新的配置即刻生效。編譯后的舊版本配置通常在varnish重啟時(shí)才會(huì)被丟棄,如果需要手動(dòng)清理,則可以使用varnishadm的vcl.discard命令完成。

4、varnish的后端存儲(chǔ)

varnish支持多種不同類型的后端存儲(chǔ),這可以在varnishd啟動(dòng)時(shí)使用-s選項(xiàng)指定。后端存儲(chǔ)的類型包括:

(1)file:使用特定的文件存儲(chǔ)全部的緩存數(shù)據(jù),并通過(guò)操作系統(tǒng)的mmap()系統(tǒng)調(diào)用將整個(gè)緩存文件映射至內(nèi)存區(qū)域(如果條件允許);

(2)malloc:使用malloc()庫(kù)調(diào)用在varnish啟動(dòng)時(shí)向操作系統(tǒng)申請(qǐng)指定大小的內(nèi)存空間以存儲(chǔ)緩存對(duì)象;

(3)persistent(experimental):與file的功能相同,但可以持久存儲(chǔ)數(shù)據(jù)(即重啟varnish數(shù)據(jù)時(shí)不會(huì)被清除);仍處于測(cè)試期;

varnish無(wú)法追蹤某緩存對(duì)象是否存入了緩存文件,從而也就無(wú)從得知磁盤(pán)上的緩存文件是否可用,因此,file存儲(chǔ)方法在varnish停止或重啟時(shí)會(huì)清除數(shù)據(jù)。而persistent方法的出現(xiàn)對(duì)此有了一個(gè)彌補(bǔ),但persistent仍處于測(cè)試階段,例如目前尚無(wú)法有效處理要緩存對(duì)象總體大小超出緩存空間的情況,所以,其僅適用于有著巨大緩存空間的場(chǎng)景。

選擇使用合適的存儲(chǔ)方式有助于提升系統(tǒng)性,從經(jīng)驗(yàn)的角度來(lái)看,建議在內(nèi)存空間足以存儲(chǔ)所有的緩存對(duì)象時(shí)使用malloc的方法,反之,file存儲(chǔ)將有著更好的性能的表現(xiàn)。然而,需要注意的是,varnishd實(shí)際上使用的空間比使用-s選項(xiàng)指定的緩存空間更大,一般說(shuō)來(lái),其需要為每個(gè)緩存對(duì)象多使用差不多1K左右的存儲(chǔ)空間,這意味著,對(duì)于100萬(wàn)個(gè)緩存對(duì)象的場(chǎng)景來(lái)說(shuō),其使用的緩存空間將超出指定大小1G左右。另外,為了保存數(shù)據(jù)結(jié)構(gòu)等,varnish自身也會(huì)占去不小的內(nèi)存空間。

為varnishd指定使用的緩存類型時(shí),-s選項(xiàng)可接受的參數(shù)格式如下:

 malloc[,size] 或

    file[,path[,size[,granularity]]] 或

 persistent,path,size {experimental}

 

file中的granularity用于設(shè)定緩存空間分配單位,默認(rèn)單位是字節(jié),所有其它的大小都會(huì)被圓整。

二、安裝varnish

wget -c http://repo.varnish-cache.org/source/varnish-3.0.1.tar.gz

tar xzvf varnish-3.0.1.tar.gz

cd varnish-3.0.1

./configure --prefix=/usr/local/varnish

make

make install

groupadd varnish

useradd -d /var/lib/varnish -g varnish -s /sbin/nologin varnish

ln -s /usr/local/varnish/sbin/varnishd /usr/sbin/varnishd

啟動(dòng)varnish:

varnishd -f /usr/local/varnish/etc/varnish/default.vcl -s malloc,1G -g varnish -u varnish -T 127.0.0.1:2000

關(guān)閉varnish:

pkill varnish

啟動(dòng)參數(shù)介紹:

-f /usr/local/etc/varnish/default.vcl

這個(gè) –f 選項(xiàng)指定varnishd使用哪個(gè)配置文件。

-s malloc,1G

這個(gè) –s 選項(xiàng)用來(lái)確定varnish使用的存儲(chǔ)類型和存儲(chǔ)容量,我使用的是malloc類型(malloc是一個(gè)C函數(shù),用于分配內(nèi)存空間), 1G 定義多少內(nèi)存被malloced,1G = 1gigabyte。

-T 127.0.0.1:2000

Varnish有一個(gè)基于文本的管理接口,啟動(dòng)它的話可以在不停止varnish的情況下來(lái)管理varnish。您可以指定管理軟件監(jiān)聽(tīng)哪個(gè)接口。當(dāng)然您不能讓全世界的人都能訪問(wèn)您的varnish管理接口,因?yàn)樗麄兛梢院茌p松的通過(guò)訪問(wèn)varnish管理接口來(lái)獲得您的root訪問(wèn)權(quán)限。我推薦只讓它監(jiān)聽(tīng)本機(jī)端口。如果您的系統(tǒng)里有您不完全信任的用戶,您可以通過(guò)防火墻規(guī)則來(lái)限制他訪問(wèn)varnish的管理端口。

-a 0.0.0.0:8080

這一句的意思是制定varnish監(jiān)聽(tīng)所有IP發(fā)給8080端口的http請(qǐng)求,如果在生產(chǎn)環(huán)境下,您應(yīng)該讓varnish監(jiān)聽(tīng)80,這也是默認(rèn)的。

vcl配置文件的介紹請(qǐng)執(zhí)行如何命令查看:

man /usr/local/varnish/share/man/man7/vcl.7

三、HTTP協(xié)議與varnish

1、緩存相關(guān)的HTTP首部

HTTP協(xié)議提供了多個(gè)首部用以實(shí)現(xiàn)頁(yè)面緩存及緩存失效的相關(guān)功能,這其中最常用的有:

(1)Expires:用于指定某web對(duì)象的過(guò)期日期/時(shí)間,通常為GMT格式;一般不應(yīng)該將此設(shè)定的未來(lái)過(guò)長(zhǎng)的時(shí)間,一年的長(zhǎng)度對(duì)大多場(chǎng)景來(lái)說(shuō)足矣;其常用于為純靜態(tài)內(nèi)容如JavaScripts樣式表或圖片指定緩存周期;

(2)Cache-Control:用于定義所有的緩存機(jī)制都必須遵循的緩存指示,這些指示是一些特定的指令,包括public、private、no-cache(表示可以存儲(chǔ),但在重新驗(yàn)正其有效性之前不能用于響應(yīng)客戶端請(qǐng)求)、no-store、max-age、s-maxage以及must-revalidate等;Cache-Control中設(shè)定的時(shí)間會(huì)覆蓋Expires中指定的時(shí)間;

(3)Etag:響應(yīng)首部,用于在響應(yīng)報(bào)文中為某web資源定義版本標(biāo)識(shí)符;

(4)Last-Mofified:響應(yīng)首部,用于回應(yīng)客戶端關(guān)于Last-Modified-Since或If-None-Match首部的請(qǐng)求,以通知客戶端其請(qǐng)求的web對(duì)象最近的修改時(shí)間;

(5)If-Modified-Since:條件式請(qǐng)求首部,如果在此首部指定的時(shí)間后其請(qǐng)求的web內(nèi)容發(fā)生了更改,則服務(wù)器響應(yīng)更改后的內(nèi)容,否則,則響應(yīng)304(not modified);

(6)If-None-Match:條件式請(qǐng)求首部;web服務(wù)器為某web內(nèi)容定義了Etag首部,客戶端請(qǐng)求時(shí)能獲取并保存這個(gè)首部的值(即標(biāo)簽);而后在后續(xù)的請(qǐng)求中會(huì)通過(guò)If-None-Match首部附加其認(rèn)可的標(biāo)簽列表并讓服務(wù)器端檢驗(yàn)其原始內(nèi)容是否有可以與此列表中的某標(biāo)簽匹配的標(biāo)簽;如果有,則響應(yīng)304,否則,則返回原始內(nèi)容;

(7)Vary:響應(yīng)首部,原始服務(wù)器根據(jù)請(qǐng)求來(lái)源的不同響應(yīng)的可能會(huì)有所不同的首部,最常用的是Vary: Accept-Encoding,用于通知緩存機(jī)制其內(nèi)容看起來(lái)可能不同于用戶請(qǐng)求時(shí)Accept-Encoding-header首部標(biāo)識(shí)的編碼格式;

(8)Age:緩存服務(wù)器可以發(fā)送的一個(gè)額外的響應(yīng)首部,用于指定響應(yīng)的有效期限;瀏覽器通常根據(jù)此首部決定內(nèi)容的緩存時(shí)長(zhǎng);如果響應(yīng)報(bào)文首部還使用了max-age指令,那么緩存的有效時(shí)長(zhǎng)為“max-age減去Age”的結(jié)果;

四、Varnish狀態(tài)引擎(state engine)

VCL用于讓管理員定義緩存策略,而定義好的策略將由varnish的management進(jìn)程分析、轉(zhuǎn)換成C代碼、編譯成二進(jìn)制程序并連接至child進(jìn)程。varnish內(nèi)部有幾個(gè)所謂的狀態(tài)(state),在這些狀態(tài)上可以附加通過(guò)VCL定義的策略以完成相應(yīng)的緩存處理機(jī)制,因此VCL也經(jīng)常被稱作“域?qū)S谩闭Z(yǔ)言或狀態(tài)引擎,“域?qū)S谩敝傅氖怯行?shù)據(jù)僅出現(xiàn)于特定的狀態(tài)中。

1、VCL狀態(tài)引擎

在VCL狀態(tài)引擎中,狀態(tài)之間具有相關(guān)性,但彼此間互相隔離,每個(gè)引擎使用return(x)來(lái)退出當(dāng)前狀態(tài)并指示varnish進(jìn)入下一個(gè)狀態(tài)。

varnish開(kāi)始處理一個(gè)請(qǐng)求時(shí),首先需要分析HTTP請(qǐng)求本身,比如從首部獲取請(qǐng)求方法、驗(yàn)正其是否為一個(gè)合法的HTT請(qǐng)求等。當(dāng)這些基本分析結(jié)束后就需要做出第一個(gè)決策,即varnish是否從緩存中查找請(qǐng)求的資源。這個(gè)決定的實(shí)現(xiàn)則需要由VCL來(lái)完成,簡(jiǎn)單來(lái)說(shuō),要由vcl_recv方法來(lái)完成。如果管理員沒(méi)有自定義vcl_recv函數(shù),varnish將會(huì)執(zhí)行默認(rèn)的vcl_recv函數(shù)。然而,即便管理員自定義了vcl_recv,但如果沒(méi)有為自定義的vcl_recv函數(shù)指定其終止操作(terminating),其仍將執(zhí)行默認(rèn)的vcl_recv函數(shù)。事實(shí)上,varnish官方強(qiáng)烈建議讓varnish執(zhí)行默認(rèn)的vcl_recv以便處理自定義vcl_recv函數(shù)中的可能出現(xiàn)的漏洞。

2、VCL語(yǔ)法

VCL的設(shè)計(jì)參考了C和Perl語(yǔ)言,因此,對(duì)有著C或Perl編程經(jīng)驗(yàn)者來(lái)說(shuō),其非常易于理解。其基本語(yǔ)法說(shuō)明如下:

(1)//、#或/* comment */用于注釋

(2)sub $name 定義函數(shù)

(3)不支持循環(huán),有內(nèi)置變量

(4)使用終止語(yǔ)句,沒(méi)有返回值

(5)域?qū)S?/span>

(6)操作符:=(賦值)、==(等值比較)、~(模式匹配)、!(取反)、&&(邏輯與)、||(邏輯或)

 

VCL的函數(shù)不接受參數(shù)并且沒(méi)有返回值,因此,其并非真正意義上的函數(shù),這也限定了VCL內(nèi)部的數(shù)據(jù)傳遞只能隱藏在HTTP首部?jī)?nèi)部進(jìn)行。VCL的return語(yǔ)句用于將控制權(quán)從VCL狀態(tài)引擎返回給Varnish,而非默認(rèn)函數(shù),這就是為什么VCL只有終止語(yǔ)句而沒(méi)有返回值的原因。同時(shí),對(duì)于每個(gè)“域”來(lái)說(shuō),可以定義一個(gè)或多個(gè)終止語(yǔ)句,以告訴Varnish下一步采取何種操作,如查詢緩存或不查詢緩存等。

3、VCL的內(nèi)置函數(shù)

VCL提供了幾個(gè)函數(shù)來(lái)實(shí)現(xiàn)字符串的修改,添加bans,重啟VCL狀態(tài)引擎以及將控制權(quán)轉(zhuǎn)回Varnish等。

regsub(str,regex,sub)

regsuball(str,regex,sub):這兩個(gè)用于基于正則表達(dá)式搜索指定的字符串并將其替換為指定的字符串;但regsuball()可以將str中能夠被regex匹配到的字符串統(tǒng)統(tǒng)替換為sub,regsub()只替換一次;

ban(expression):

ban_url(regex):Bans所有其URL能夠由regex匹配的緩存對(duì)象;

purge:從緩存中挑選出某對(duì)象以及其相關(guān)變種一并刪除,這可以通過(guò)HTTP協(xié)議的PURGE方法完成;

hash_data(str):

return():當(dāng)某VCL域運(yùn)行結(jié)束時(shí)將控制權(quán)返回給Varnish,并指示Varnish如何進(jìn)行后續(xù)的動(dòng)作;其可以返回的指令包括:lookup、pass、pipe、hit_for_pass、fetch、deliver和hash等;但某特定域可能僅能返回某些特定的指令,而非前面列出的全部指令;

return(restart):重新運(yùn)行整個(gè)VCL,即重新從vcl_recv開(kāi)始進(jìn)行處理;每一次重啟都會(huì)增加req.restarts變量中的值,而max_restarts參數(shù)則用于限定最大重啟次數(shù)。

4、vcl_recv

vcl_recv是在Varnish完成對(duì)請(qǐng)求報(bào)文的解碼為基本數(shù)據(jù)結(jié)構(gòu)后第一個(gè)要執(zhí)行的子例程,它通常有四個(gè)主要用途:

(1)修改客戶端數(shù)據(jù)以減少緩存對(duì)象差異性;比如刪除URL中的www.等字符;

(2)基于客戶端數(shù)據(jù)選用緩存策略;比如僅緩存特定的URL請(qǐng)求、不緩存POST請(qǐng)求等;

(3)為某web應(yīng)用程序執(zhí)行URL重寫(xiě)規(guī)則;

(4)挑選合適的后端Web服務(wù)器;

可以使用下面的終止語(yǔ)句,即通過(guò)return()向Varnish返回的指示操作:

pass:繞過(guò)緩存,即不從緩存中查詢內(nèi)容或不將內(nèi)容存儲(chǔ)至緩存中;

pipe:不對(duì)客戶端進(jìn)行檢查或做出任何操作,而是在客戶端與后端服務(wù)器之間建立專用“管道”,并直接將數(shù)據(jù)在二者之間進(jìn)行傳送;此時(shí),keep-alive連接中后續(xù)傳送的數(shù)據(jù)也都將通過(guò)此管道進(jìn)行直接傳送,并不會(huì)出現(xiàn)在任何日志中;

lookup:在緩存中查找用戶請(qǐng)求的對(duì)象,如果緩存中沒(méi)有其請(qǐng)求的對(duì)象,后續(xù)操作很可能會(huì)將其請(qǐng)求的對(duì)象進(jìn)行緩存;

error:由Varnish自己合成一個(gè)響應(yīng)報(bào)文,一般是響應(yīng)一個(gè)錯(cuò)誤類信息、重定向類信息或負(fù)載均衡器返回的后端web服務(wù)器健康狀態(tài)檢查類信息;

vcl_recv也可以通過(guò)精巧的策略完成一定意義上的安全功能,以將某些特定的攻擊扼殺于搖籃中。同時(shí),它也可以檢查出一些拼寫(xiě)類的錯(cuò)誤并將其進(jìn)行修正等。

Varnish默認(rèn)的vcl_recv專門(mén)設(shè)計(jì)用來(lái)實(shí)現(xiàn)安全的緩存策略,它主要完成兩種功能:

(1)僅處理可以識(shí)別的HTTP方法,并且只緩存GET和HEAD方法;

(2)不緩存任何用戶特有的數(shù)據(jù);

安全起見(jiàn),一般在自定義的vcl_recv中不要使用return()終止語(yǔ)句,而是再由默認(rèn)vcl_recv進(jìn)行處理,并由其做出相應(yīng)的處理決策。

下面是一個(gè)自定義的使用示例:

sub vcl_recv {

 if (req.http.User-Agent ~ "iPad" ||

 req.http.User-Agent ~ "iPhone" ||

 req.http.User-Agent ~ "Android") {

 set req.http.X-Device = "mobile";

 } else {

 set req.http.X-Device = "desktop";

 }

}

此例中的VCL創(chuàng)建一個(gè)X-Device請(qǐng)求首部,其值可能為mobile或desktop,于是web服務(wù)器可以基于此完成不同類型的響應(yīng),以提高用戶體驗(yàn)。

5、vcl_fetch

如前面所述,相對(duì)于vcl_recv是根據(jù)客戶端的請(qǐng)求作出緩存決策來(lái)說(shuō),vcl_fetch則是根據(jù)服務(wù)器端的響應(yīng)作出緩存決策。在任何VCL狀態(tài)引擎中返回的pass操作都將由vcl_fetch進(jìn)行后續(xù)處理。vcl_fetch中有許多可用的內(nèi)置變量,比如最常用的用于定義某對(duì)象緩存時(shí)長(zhǎng)的beresp.ttl變量。通過(guò)return()返回給arnish的操作指示有:

(1)deliver:緩存此對(duì)象,并將其發(fā)送給客戶端(經(jīng)由vcl_deliver);

(2)hit_for_pass:不緩存此對(duì)象,但可以導(dǎo)致后續(xù)對(duì)此對(duì)象的請(qǐng)求直接送達(dá)到vcl_pass進(jìn)行處理;

(3)restart:重啟整個(gè)VCL,并增加重啟計(jì)數(shù);超出max_restarts限定的最大重啟次數(shù)后將會(huì)返回錯(cuò)誤信息;

(4)error code [reason]:返回指定的錯(cuò)誤代碼給客戶端并丟棄此請(qǐng)求;

默認(rèn)的vcl_fetch放棄了緩存任何使用了Set-Cookie首部的響應(yīng)。

五、修剪緩存對(duì)象

1、緩存內(nèi)容修剪

提高緩存命中率的最有效途徑之一是增加緩存對(duì)象的生存時(shí)間(TTL),但是這也可能會(huì)帶來(lái)副作用,比如緩存的內(nèi)容在到達(dá)為其指定的有效期之間已經(jīng)失效。因此,手動(dòng)檢驗(yàn)緩存對(duì)象的有效性或者刷新緩存是緩存很有可能成為服務(wù)器管理員的日常工作之一,相應(yīng)地,Varnish為完成這類的任務(wù)提供了三種途徑:HTTP 修剪(HTTP purging)、禁用某類緩存對(duì)象(banning)和強(qiáng)制緩存未命令(forced cache misses)。

這里需要特殊說(shuō)明的是,Varnish 2中的purge()操作在Varnish 3中被替換為了ban()操作,而Varnish 3也使用了purge操作,但為其賦予了新的功能,且只能用于vcl_hit或vcl_miss中替換Varnish 2中常用的set obj.ttl=0s。

在具體執(zhí)行某清理工作時(shí),需要事先確定如下問(wèn)題:

(1)僅需要檢驗(yàn)一個(gè)特定的緩存對(duì)象,還是多個(gè)?

(2)目的是釋放內(nèi)存空間,還是僅替換緩存的內(nèi)容?

(3)是不是需要很長(zhǎng)時(shí)間才能完成內(nèi)容替換?

(4)這類操作是個(gè)日常工作,還是僅此一次的特殊需求?

2、移除單個(gè)緩存對(duì)象

purge用于清理緩存中的某特定對(duì)象及其變種(variants),因此,在有著明確要修剪的緩存對(duì)象時(shí)可以使用此種方式。HTTP協(xié)議的PURGE方法可以實(shí)現(xiàn)purge功能,不過(guò),其僅能用于vcl_hit和vcl_miss中,它會(huì)釋放內(nèi)存工作并移除指定緩存對(duì)象的所有Vary:-變種,并等待下一個(gè)針對(duì)此內(nèi)容的客戶端請(qǐng)求到達(dá)時(shí)刷新此內(nèi)容。另外,其一般要與return(restart)一起使用。下面是個(gè)在VCL中配置的示例。

acl purgers {

 "127.0.0.1";

 "192.168.0.0"/24;

}

sub vcl_recv {

 if (req.request == "PURGE") {

 if (!client.ip ~ purgers) {

 error 405 "Method not allowed";

 }

 return (lookup);

 }

}

sub vcl_hit {

 if (req.request == "PURGE") {

 purge;

 error 200 "Purged";

 }

}

sub vcl_miss {

 if (req.request == "PURGE") {

 purge;

 error 404 "Not in cache";

 }

}

sub vcl_pass {

 if (req.request == "PURGE") {

 error 502 "PURGE on a passed object";

 }

}

客戶端在發(fā)起HTTP請(qǐng)求時(shí),只需要為所請(qǐng)求的URL使用PURGE方法即可,其命令使用方式如下:

# curl -I PURGE http://varniship/path/to/someurl

3、強(qiáng)制緩存未命中

在vcl_recv中使用return(pass)能夠強(qiáng)制到上游服務(wù)器取得請(qǐng)求的內(nèi)容,但這也會(huì)導(dǎo)致無(wú)法將其緩存。使用purge會(huì)移除舊的緩存對(duì)象,但如果上游服務(wù)器宕機(jī)而無(wú)法取得新版本的內(nèi)容時(shí),此內(nèi)容將無(wú)法再響應(yīng)給客戶端。使用req.has_always_miss=ture,可以讓Varnish在緩存中搜尋相應(yīng)的內(nèi)容但卻總是回應(yīng)“未命中”,于是vcl_miss將后續(xù)地負(fù)責(zé)啟動(dòng)vcl_fetch從上游服務(wù)器取得新內(nèi)容,并以新內(nèi)容緩存覆蓋舊內(nèi)容。此時(shí),如果上游服務(wù)器宕機(jī)或未響應(yīng),舊的內(nèi)容將保持原狀,并能夠繼續(xù)服務(wù)于那些未使用req.has_always_miss=true的客戶端,直到其過(guò)期失效或由其它方法移除。

4、Banning

ban()是一種從已緩存對(duì)象中過(guò)濾(filter)出某此特定的對(duì)象并將其移除的緩存內(nèi)容刷新機(jī)制,不過(guò),它并不阻止新的內(nèi)容進(jìn)入緩存或響應(yīng)于請(qǐng)求。在Varnish中,ban的實(shí)現(xiàn)是指將一個(gè)ban添加至ban列表(ban-list)中,這可以通過(guò)命令行接口或VCL實(shí)現(xiàn),它們的使用語(yǔ)法是相同的。ban本身就是一個(gè)或多個(gè)VCL風(fēng)格的語(yǔ)句,它會(huì)在Varnish從緩存哈希(cache hash)中查找某緩存對(duì)象時(shí)對(duì)搜尋的對(duì)象進(jìn)行比較測(cè)試,因此,一個(gè)ban語(yǔ)句就是類似匹配所有“以/downloads開(kāi)頭的URL”,或“響應(yīng)首部中包含nginx的對(duì)象”。例如:

 ban req.http.host == "magedu.com" && req.url ~ "\.gif$"

 

定義好的所有ban語(yǔ)句會(huì)生成一個(gè)ban列表(ban-list),新添加的ban語(yǔ)句會(huì)被放置在列表的首部。緩存中的所有對(duì)象在響應(yīng)給客戶端之前都會(huì)被ban列表檢查至少一次,檢查完成后將會(huì)為每個(gè)緩存創(chuàng)建一個(gè)指向與其匹配的ban語(yǔ)句的指針。Varnish在從緩存中獲取對(duì)象時(shí),總是會(huì)檢查此緩存對(duì)象的指針是否指向了ban列表的首部。如果沒(méi)有指向ban列表的首部,其將對(duì)使用所有的新添加的ban語(yǔ)句對(duì)此緩存對(duì)象進(jìn)行測(cè)試,如果沒(méi)有任何ban語(yǔ)句能夠匹配,則更新ban列表。

對(duì)ban這種實(shí)現(xiàn)方式持反對(duì)意見(jiàn)有有之,持贊成意見(jiàn)者亦有之。反對(duì)意見(jiàn)主要有兩種,一是ban不會(huì)釋放內(nèi)存,緩存對(duì)象僅在有客戶端訪問(wèn)時(shí)被測(cè)試一次;二是如果緩存對(duì)象曾經(jīng)被訪問(wèn)到,但卻很少被再次訪問(wèn)時(shí)ban列表將會(huì)變得非常大。贊成的意見(jiàn)則主要集中在ban可以讓Varnish在恒定的時(shí)間內(nèi)完成向ban列表添加ban的操作,例如在有著數(shù)百萬(wàn)個(gè)緩存對(duì)象的場(chǎng)景中,添加一個(gè)ban也只需要在恒定的時(shí)間內(nèi)即可完成。其實(shí)現(xiàn)方法本處不再詳細(xì)說(shuō)明。

六、Varnish檢測(cè)后端主機(jī)的健康狀態(tài)

Varnish可以檢測(cè)后端主機(jī)的健康狀態(tài),在判定后端主機(jī)失效時(shí)能自動(dòng)將其從可用后端主機(jī)列表中移除,而一旦其重新變得可用還可以自動(dòng)將其設(shè)定為可用。為了避免誤判,Varnish在探測(cè)后端主機(jī)的健康狀態(tài)發(fā)生轉(zhuǎn)變時(shí)(比如某次探測(cè)時(shí)某后端主機(jī)突然成為不可用狀態(tài)),通常需要連續(xù)執(zhí)行幾次探測(cè)均為新?tīng)顟B(tài)才將其標(biāo)記為轉(zhuǎn)換后的狀態(tài)。

每個(gè)后端服務(wù)器當(dāng)前探測(cè)的健康狀態(tài)探測(cè)方法通過(guò).probe進(jìn)行設(shè)定,其結(jié)果可由req.backend.healthy變量獲取,也可通過(guò)varnishlog中的Backend_health查看或varnishadm的debug.health查看。

backend web1 {

 .host = "www.magedu.com";

 .probe = {

 .url = "/.healthtest.html";

 .interval = 1s;

 .window = 5;

 .threshold = 2;

 }

}

.probe中的探測(cè)指令常用的有:

(1) .url:探測(cè)后端主機(jī)健康狀態(tài)時(shí)請(qǐng)求的URL,默認(rèn)為“/”;

(2) .request: 探測(cè)后端主機(jī)健康狀態(tài)時(shí)所請(qǐng)求內(nèi)容的詳細(xì)格式,定義后,它會(huì)替換.url指定的探測(cè)方式;比如:

 .request =

 "GET /.healthtest.html HTTP/1.1"

 "Host: www.magedu.com"

 "Connection: close";

(3) .window:設(shè)定在判定后端主機(jī)健康狀態(tài)時(shí)基于最近多少次的探測(cè)進(jìn)行,默認(rèn)是8;

(4) .threshold:在.window中指定的次數(shù)中,至少有多少次是成功的才判定后端主機(jī)正健康運(yùn)行;默認(rèn)是3;

(5) .initial:Varnish啟動(dòng)時(shí)對(duì)后端主機(jī)至少需要多少次的成功探測(cè),默認(rèn)同.threshold;

(6) .expected_response:期望后端主機(jī)響應(yīng)的狀態(tài)碼,默認(rèn)為200;

(7) .interval:探測(cè)請(qǐng)求的發(fā)送周期,默認(rèn)為5秒;

(8) .timeout:每次探測(cè)請(qǐng)求的過(guò)期時(shí)長(zhǎng),默認(rèn)為2秒;

因此,如上示例中表示每隔1秒對(duì)此后端主機(jī)www.magedu.com探測(cè)一次,請(qǐng)求的URL為http://www.magedu.com/.healthtest.html,在最近5次的探測(cè)請(qǐng)求中至少有2次是成功的(響應(yīng)碼為200)就判定此后端主機(jī)為正常工作狀態(tài)。

如果Varnish在某時(shí)刻沒(méi)有任何可用的后端主機(jī),它將嘗試使用緩存對(duì)象的“寬容副本”(graced copy),當(dāng)然,此時(shí)VCL中的各種規(guī)則依然有效。因此,更好的辦法是在VCL規(guī)則中判斷req.backend.healthy變量顯示某后端主機(jī)不可用時(shí),為此后端主機(jī)增大req.grace變量的值以設(shè)定適用的寬容期限長(zhǎng)度。

七、Varnish使用多臺(tái)后端主機(jī)

Varnish中可以使用director指令將一個(gè)或多個(gè)近似的后端主機(jī)定義為一個(gè)邏輯組,并可以指定的調(diào)度方式(也叫挑選方法)來(lái)輪流將請(qǐng)求發(fā)送至這些主機(jī)上。不同的director可以使用同一個(gè)后端主機(jī),而某director也可以使用“匿名”后端主機(jī)(在director中直接進(jìn)行定義)。每個(gè)director都必須有其專用名,且在定義后必須在VCL中進(jìn)行調(diào)用,VCL中任何可以指定后端主機(jī)的位置均可以按需將其替換為調(diào)用某已定義的director。

backend web1 {

 .host = "backweb1.magedu.com";

 .port = "80";

}

director webservers random {

  .retries = 5;

  {

    .backend = web1;

    .weight  = 2;

  }

  {

    .backend  = {

      .host = "backweb2.magedu.com";

   .port = "80";

    }

  .weight         = 3;

  }

}

如上示例中,web1為顯式定義的后端主機(jī),而webservers這個(gè)directors還包含了一個(gè)“匿名”后端主機(jī)(backweb2.magedu.com)。webservers從這兩個(gè)后端主機(jī)中挑選一個(gè)主機(jī)的方法為random,即以隨機(jī)方式挑選。

Varnish的director支持的挑選方法中比較簡(jiǎn)單的有round-robin和random兩種。其中,round-robin類型沒(méi)有任何參數(shù),只需要為其指定各后端主機(jī)即可,挑選方式為“輪叫”,并在某后端主機(jī)故障時(shí)不再將其視作挑選對(duì)象;random方法隨機(jī)從可用后端主機(jī)中進(jìn)行挑選,每一個(gè)后端主機(jī)都需要一個(gè).weight參數(shù)以指定其權(quán)重,同時(shí)還可以director級(jí)別使用.retires參數(shù)來(lái)設(shè)定查找一個(gè)健康后端主機(jī)時(shí)的嘗試次數(shù)。

Varnish 2.1.0后,random挑選方法又多了兩種變化形式client和hash。client類型的director使用client.identity作為挑選因子,這意味著client.identity相同的請(qǐng)求都將被發(fā)送至同一個(gè)后端主機(jī)。client.identity默認(rèn)為cliet.ip,但也可以在VCL中將其修改為所需要的標(biāo)識(shí)符。類似地,hash類型的director使用hash數(shù)據(jù)作為挑選因子,這意味著對(duì)同一個(gè)URL的請(qǐng)求將被發(fā)往同一個(gè)后端主機(jī),其常用于多級(jí)緩存的場(chǎng)景中。然而,無(wú)論是client還hash,當(dāng)其傾向于使用后端主機(jī)不可用時(shí)將會(huì)重新挑選新的后端其機(jī)。

八、varnish管理進(jìn)階

1、可調(diào)參數(shù)

Varnish有許多參數(shù),雖然大多數(shù)場(chǎng)景中這些參數(shù)的默認(rèn)值都可以工作得很好,然而特定的工作場(chǎng)景中要想有著更好的性能的表現(xiàn),則需要調(diào)整某些參數(shù)??梢栽诠芾斫涌谥惺褂胮aram.show命令查看這些參數(shù),而使用param.set則能修改這些參數(shù)的值。然而,在命令行接口中進(jìn)行的修改不會(huì)保存至任何位置,因此,重啟varnish后這些設(shè)定會(huì)消失。此時(shí),可以通過(guò)啟動(dòng)腳本使用-p選項(xiàng)在varnishd啟動(dòng)時(shí)為其設(shè)定參數(shù)的值。然而,除非特別需要對(duì)其進(jìn)行修改,保持這些參數(shù)為默認(rèn)值可以有效降低管理復(fù)雜度。

2、共享內(nèi)存日志

共享內(nèi)存日志(shared memory log)通常被簡(jiǎn)稱為shm-log,它用于記錄日志相關(guān)的數(shù)據(jù),大小為80M。varnish以輪轉(zhuǎn)(round-robin)的方式使用其存儲(chǔ)空間。一般不需要對(duì)shm-log做出更多的設(shè)定,但應(yīng)該避免其產(chǎn)生I/O,這可以使用tmpfs實(shí)現(xiàn),其方法為在/etc/fstab中設(shè)定一個(gè)掛載至/var/lib/varnish目錄(或其它自定義的位置)臨時(shí)文件系統(tǒng)即可。

3、線程模型(Trheading model)

varnish的child進(jìn)程由多種不同的線程組成,分別用于完成不同的工作。例如:

 cache-worker線程:每連接一個(gè),用于處理請(qǐng)求;

 cache-main線程:全局只有一個(gè),用于啟動(dòng)cache;

 ban lurker線程:一個(gè),用于清理bans;

 acceptor線程:一個(gè),用于接收新的連接請(qǐng)求;

 epoll/kqueue線程:數(shù)量可配置,默認(rèn)為2,用于管理線程池;

 expire線程:一個(gè),用于移除老化的內(nèi)容;

 backend poll線程:每個(gè)后端服務(wù)器一個(gè),用于檢測(cè)后端服務(wù)器的健康狀況;

在配置varnish時(shí),一般只需為關(guān)注cache-worker線程,而且也只能配置其線程池的數(shù)量,而除此之外的其它均非可配置參數(shù)。與此同時(shí),線程池的數(shù)量也只能在流量較大的場(chǎng)景下才需要增加,而且經(jīng)驗(yàn)表明其多于2個(gè)對(duì)提升性能并無(wú)益處。

4、線程相關(guān)的參數(shù)(Threading parameters)

varnish為每個(gè)連接使用一個(gè)線程,因此,其worker線程的最大數(shù)決定了varnish的并發(fā)響應(yīng)能力。下面是線程池相關(guān)的各參數(shù)及其配置:

thread_pool_add_delay      2 [milliseconds]

thread_pool_add_threshold  2 [requests]

thread_pool_fail_delay     200 [milliseconds]

thread_pool_max            500 [threads]

thread_pool_min            5 [threads]

thread_pool_purge_delay    1000 [milliseconds]

thread_pool_stack          65536 [bytes]

thread_pool_timeout        120 [seconds]

thread_pool_workspace      16384 [bytes]

thread_pools               2 [pools]

thread_stats_rate          10 [requests]

其中最關(guān)鍵的當(dāng)屬thread_pool_max和thread_pool_min,它們分別用于定義每個(gè)線程池中的最大線程數(shù)和最少線程數(shù)。因此,在某個(gè)時(shí)刻,至少有thread_pool_min*thread_pools個(gè)worker線程在運(yùn)行,但至多不能超出thread_pool_max*thread_pools個(gè)。根據(jù)需要,這兩個(gè)參數(shù)的數(shù)量可以進(jìn)行調(diào)整,varnishstat命令的n_wrk_queued可以顯示當(dāng)前varnish的線程數(shù)量是否足夠,如果隊(duì)列中始終有不少的線程等待運(yùn)行,則可以適當(dāng)調(diào)大thread_pool_max參數(shù)的值。但一般建議每臺(tái)varnish服務(wù)器上最多運(yùn)行的worker線程數(shù)不要超出5000個(gè)。

當(dāng)某連接請(qǐng)求到達(dá)時(shí),varnish選擇一個(gè)線程池負(fù)責(zé)處理此請(qǐng)求。而如果此線程池中的線程數(shù)量已經(jīng)達(dá)到最大值,新的請(qǐng)求將會(huì)被放置于隊(duì)列中或被直接丟棄。默認(rèn)線程池的數(shù)量為2,這對(duì)最繁忙的varnish服務(wù)器來(lái)說(shuō)也已經(jīng)足夠。

5、

九、Varnish的命令行工具

1、varnishadm命令

命令語(yǔ)法:varnishadm [-t timeout] [-S secret_file] [-T address:port] [-n name] [command [...]]

通過(guò)命令行的方式連接至varnishd進(jìn)行管理操作的工具,指定要連接的varnish實(shí)例的方法有兩種:

-n name —— 連接至名稱為“name”的實(shí)例;

-T address:port —— 連接至指定套接字上的實(shí)例;

其運(yùn)行模式有兩種,當(dāng)不在命令行中給出要執(zhí)行的"command"時(shí),其將進(jìn)入交互式模式;否則,varnishadm將執(zhí)行指定的"command"并退出。要查看本地啟用的緩存,可使用如下命令進(jìn)行。

# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 storage.list

sub vcl_deliver {

  if (obj.hits > 0) {

    set resp.http.X-Cache = "HIT";

  } else {

    set resp.http.X-Cache = "MISS";

  }

}

sub vcl_deliver {

        if (obj.hits > 0) {

                set resp.http.X-Cache = "HIT via" + " " + server.hostname;

        } else {

                set resp.http.X-Cache = "MISS via" + " " + server.hostname;

        }

}

sub vcl_recv {

        if (req.url ~ "^/test.html$") {

                return(pass);

        }

}

sub vcl_fetch {

        if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg)$") {

                set beresp.ttl = 3600s;

        }

}

sub vcl_fetch {

 if (beresp.http.cache-control !~ "s-maxage") {

 if (req.url ~ "\.jpg(\?|$)") {

 set beresp.ttl = 30s;

 unset beresp.http.Set-Cookie;

 }

 if (req.url ~ "\.html(\?|$)") {

 set beresp.ttl = 10s;

 unset beresp.http.Set-Cookie;

 }

 } else {

 if (beresp.ttl > 0s) {

 unset beresp.http.Set-Cookie;

 }

 }

}

sub vcl_error {

 synthetic "<html><body><!-- Something was wrong! --></body></html>";

 set obj.status = 200;

 return (deliver);

}

1.1, 

request

response

Cache-Control   = "Cache-Control" ":" 1#cache-directive

    cache-directive = cache-request-directive

         | cache-response-directive

    cache-request-directive =

           "no-cache"                          

         | "no-store" (backup)                          

         | "max-age" "=" delta-seconds         

         | "max-stale" [ "=" delta-seconds ]  

         | "min-fresh" "=" delta-seconds      

         | "no-transform"                      

         | "only-if-cached"                   

         | cache-extension                   

     cache-response-directive =

           "public"                               

         | "private" [ "=" <"> 1#field-name <"> ] 

         | "no-cache" [ "=" <"> 1#field-name <"> ]

         | "no-store"                            

         | "no-transform"                         

         | "must-revalidate"                     

         | "proxy-revalidate"                    

         | "max-age" "=" delta-seconds            

         | "s-maxage" "=" delta-seconds           

         | cache-extension                        

sub vcl_deliver {

        set resp.http.X-Age = resp.http.Age;

        unset resp.http.Age;

        if (obj.hits > 0) {

                set resp.http.X-Cache = “HIT”;

        } else {

                set resp.http.X-Cache = “MISS”;

        }

}

acl purgers {

        “127.0.0.1”;

        “192.168.0.0”/24;

}

sub vcl_recv {

        if (req.request == “PURGE”) {

                if (!client.ip ~ purgers) {

                        error 405 “Method not allowed”;

                }

                return (lookup);

        }

}

sub vcl_hit {

        if (req.request == “PURGE”) {

                purge;

                error 200 “Purged”;

        }

}

sub vcl_miss {

        if (req.request == “PURGE”) { 

                purge;

                error 404 “Not in cache”;

        }

}

sub vcl_pass {

        if (req.request == “PURGE”) {

                error 502 “PURGE on a passed object”;

        }

}

sed /pattern/string/g

在請(qǐng)求的時(shí)候使用curl -H PURGE URL

regsub(str, regex, sub)

regsub($req.url, www\.maged\.com, www.mageedu.com)

purge: 讓緩存失效的;

ban()

URL rewrite

VCL - functions

  regsub(str, regex, sub)

  regsuball(str, regex, sub)

  ban_url(regex)

  ban(expression)

  purge;

  return(restart)

  return()

  hash_data()

子例程

vcl_recv {

}

cookie

# Drop any cookies sent to WordPress.

 sub vcl_recv {

 if (!(req.url ~ "wp-(login|admin)")) {

 unset req.http.cookie;

 }

 }

 

 # Drop any cookies WordPress tries to send back to the client.

 sub vcl_fetch {

 if (!(req.url ~ "wp-(login|admin)")) {

 unset beresp.http.set-cookie;

 }

 }

【騰訊云】云服務(wù)器、云數(shù)據(jù)庫(kù)、COS、CDN、短信等云產(chǎn)品特惠熱賣(mài)中

發(fā)表評(píng)論

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: