1、關於Varnishphp
一、varnish系統架構css
varnish主要運行兩個進程:Management進程和Child進程(也叫Cache進程)。
html
Management進程主要實現應用新的配置、編譯VCL、監控varnish、初始化varnish以及提供一個命令行接口等。Management進程會每隔幾秒鐘探測一下Child進程以判斷其是否正常運行,若是在指定的時長內未獲得Child進程的迴應,Management將會重啓此Child進程。
linux
Child進程包含多種類型的線程,常見的如:
web
Acceptor線程:接收新的鏈接請求並響應;正則表達式
Worker線程:child進程會爲每一個會話啓動一個worker線程,所以,在高併發的場景中可能會出現數百個worker線程甚至更多;算法
Expiry線程:從緩存中清理過時內容;express
Varnish依賴「工做區(workspace)」以下降線程在申請或修改內存時出現競爭的可能性。在varnish內部有多種不一樣的工做區,其中最關鍵的當屬用於管理會話數據的session工做區。apache
二、varnish日誌編程
爲了與系統的其它部分進行交互,Child進程使用了能夠經過文件系統接口進行訪問的共享內存日誌(shared memory log),所以,若是某線程須要記錄信息,其僅須要持有一個鎖,然後向共享內存中的某內存區域寫入數據,再釋放持有的鎖便可。而爲了減小競爭,每一個worker線程都使用了日誌數據緩存。
共享內存日誌大小通常爲90M,其分爲兩部分,前一部分爲計數器,後半部分爲客戶端請求的數據。varnish提供了多個不一樣的工具如varnishlog、varnishncsa或varnishstat等來分析共享內存日誌中的信息並可以以指定的方式進行顯示。
三、VCL
Varnish Configuration Language (VCL)是varnish配置緩存策略的工具,它是一種基於「域」(domain specific)的簡單編程語言,它支持有限的算術運算和邏輯運算操做、容許使用正則表達式進行字符串匹配、容許用戶使用set自定義變量、支持if判斷語句,也有內置的函數和變量等。使用VCL編寫的緩存策略一般保存至.vcl文件中,其須要編譯成二進制的格式後才能由varnish調用。事實上,整個緩存策略就是由幾個特定的子例程如vcl_recv、vcl_fetch等組成,它們分別在不一樣的位置(或時間)執行,若是沒有事先爲某個位置自定義子例程,varnish將會執行默認的定義。
VCL策略在啓用前,會由management進程將其轉換爲C代碼,然後再由gcc編譯器將C代碼編譯成二進制程序。編譯完成後,management負責將其鏈接至varnish實例,即child進程。正是因爲編譯工做在child進程以外完成,它避免了裝載錯誤格式VCL的風險。所以,varnish修改配置的開銷很是小,其能夠同時保有幾份尚在引用的舊版本配置,也可以讓新的配置即刻生效。編譯後的舊版本配置一般在varnish重啓時纔會被丟棄,若是須要手動清理,則可使用varnishadm的vcl.discard命令完成。
四、varnish的後端存儲
varnish支持多種不一樣類型的後端存儲,這能夠在varnishd啓動時使用-s選項指定。後端存儲的類型包括:
(1)file:使用特定的文件存儲所有的緩存數據,並經過操做系統的mmap()系統調用將整個緩存文件映射至內存區域(若是條件容許);
(2)malloc:使用malloc()庫調用在varnish啓動時向操做系統申請指定大小的內存空間以存儲緩存對象;
(3)persistent(experimental):與file的功能相同,但能夠持久存儲數據(即重啓varnish數據時不會被清除);仍處於測試期;
varnish沒法追蹤某緩存對象是否存入了緩存文件,從而也就無從得知磁盤上的緩存文件是否可用,所以,file存儲方法在varnish中止或重啓時會清除數據。而persistent方法的出現對此有了一個彌補,但persistent仍處於測試階段,例如目前尚沒法有效處理要緩存對象整體大小超出緩存空間的狀況,因此,其僅適用於有着巨大緩存空間的場景。
選擇使用合適的存儲方式有助於提高系統性,從經驗的角度來看,建議在內存空間足以存儲全部的緩存對象時使用malloc的方法,反之,file存儲將有着更好的性能的表現。然而,須要注意的是,varnishd實際上使用的空間比使用-s選項指定的緩存空間更大,通常說來,其須要爲每一個緩存對象多使用差很少1K左右的存儲空間,這意味着,對於100萬個緩存對象的場景來講,其使用的緩存空間將超出指定大小1G左右。另外,爲了保存數據結構等,varnish自身也會佔去不小的內存空間。
2、HTTP協議與varnish
一、緩存相關的HTTP首部
HTTP協議提供了多個首部用以實現頁面緩存及緩存失效的相關功能,這其中最經常使用的有:
(1)Expires:用於指定某web對象的過時日期/時間,一般爲GMT格式;通常不該該將此設定的將來過長的時間,一年的長度對大多場景來講足矣;其經常使用於爲純靜態內容如JavaScripts樣式表或圖片指定緩存週期;
(2)Cache-Control:用於定義全部的緩存機制都必須遵循的緩存指示,這些指示是一些特定的指令,包括public、private、no-cache(表示能夠存儲,但在從新驗正其有效性以前不能用於響應客戶端請求)、no-store、max-age、s-maxage以及must-revalidate等;Cache-Control中設定的時間會覆蓋Expires中指定的時間;
(3)Etag:響應首部,用於在響應報文中爲某web資源定義版本標識符;
(4)Last-Mofified:響應首部,用於迴應客戶端關於Last-Modified-Since或If-None-Match首部的請求,以通知客戶端其請求的web對象最近的修改時間;
(5)If-Modified-Since:條件式請求首部,若是在此首部指定的時間後其請求的web內容發生了更改,則服務器響應更改後的內容,不然,則響應304(not modified);
(6)If-None-Match:條件式請求首部;web服務器爲某web內容定義了Etag首部,客戶端請求時能獲取並保存這個首部的值(即標籤);然後在後續的請求中會經過If-None-Match首部附加其承認的標籤列表並讓服務器端檢驗其原始內容是否有能夠與此列表中的某標籤匹配的標籤;若是有,則響應304,不然,則返回原始內容;
(7)Vary:響應首部,原始服務器根據請求來源的不一樣響應的可能會有所不一樣的首部,最經常使用的是Vary: Accept-Encoding,用於通知緩存機制其內容看起來可能不一樣於用戶請求時Accept-Encoding-header首部標識的編碼格式;
(8)Age:緩存服務器能夠發送的一個額外的響應首部,用於指定響應的有效期限;瀏覽器一般根據此首部決定內容的緩存時長;若是響應報文首部還使用了max-age指令,那麼緩存的有效時長爲「max-age減去Age」的結果;
3、Varnish狀態引擎(state engine)
VCL用於讓管理員定義緩存策略,而定義好的策略將由varnish的management進程分析、轉換成C代碼、編譯成二進制程序並鏈接至child進程。varnish內部有幾個所謂的狀態(state),在這些狀態上能夠附加經過VCL定義的策略以完成相應的緩存處理機制,所以VCL也常常被稱做「域專用」語言或狀態引擎,「域專用」指的是有些數據僅出現於特定的狀態中。
一、VCL狀態引擎
在VCL狀態引擎中,狀態之間具備相關性,但彼此間互相隔離,每一個引擎使用return(x)來退出當前狀態並指示varnish進入下一個狀態。
varnish開始處理一個請求時,首先須要分析HTTP請求自己,好比從首部獲取請求方法、驗正其是否爲一個合法的HTT請求等。當這些基本分析結束後就須要作出第一個決策,即varnish是否從緩存中查找請求的資源。這個決定的實現則須要由VCL來完成,簡單來講,要由vcl_recv方法來完成。若是管理員沒有自定義vcl_recv函數,varnish將會執行默認的vcl_recv函數。然而,即使管理員自定義了vcl_recv,但若是沒有爲自定義的vcl_recv函數指定其終止操做(terminating),其仍將執行默認的vcl_recv函數。事實上,varnish官方強烈建議讓varnish執行默認的vcl_recv以便處理自定義vcl_recv函數中的可能出現的漏洞。
二、VCL語法
VCL的設計參考了C和Perl語言,所以,對有着C或Perl編程經驗者來講,其很是易於理解。其基本語法說明以下:
(1)//、#或/* comment */用於註釋
(2)sub $name 定義函數
(3)不支持循環,有內置變量
(4)使用終止語句,沒有返回值
(5)域專用
(6)操做符:=(賦值)、==(等值比較)、~(模式匹配)、!(取反)、&&(邏輯與)、||(邏輯或)
VCL的函數不接受參數而且沒有返回值,所以,其並不是真正意義上的函數,這也限定了VCL內部的數據傳遞只能隱藏在HTTP首部內部進行。VCL的return語句用於將控制權從VCL狀態引擎返回給Varnish,而非默認函數,這就是爲何VCL只有終止語句而沒有返回值的緣由。同時,對於每一個「域」來講,能夠定義一個或多個終止語句,以告訴Varnish下一步採起何種操做,如查詢緩存或不查詢緩存等。
三、VCL的內置函數
VCL提供了幾個函數來實現字符串的修改,添加bans,重啓VCL狀態引擎以及將控制權轉回Varnish等。
regsub(str,regex,sub)
regsuball(str,regex,sub):這兩個用於基於正則表達式搜索指定的字符串並將其替換爲指定的字符串;但regsuball()能夠將str中可以被regex匹配到的字符串通通替換爲sub,regsub()只替換一次;
ban(expression):
ban_url(regex):Bans全部其URL可以由regex匹配的緩存對象;
purge:從緩存中挑選出某對象以及其相關變種一併刪除,這能夠經過HTTP協議的PURGE方法完成;
hash_data(str):
return():當某VCL域運行結束時將控制權返回給Varnish,並指示Varnish如何進行後續的動做;其能夠返回的指令包括:lookup、pass、pipe、hit_for_pass、fetch、deliver和hash等;但某特定域可能僅能返回某些特定的指令,而非前面列出的所有指令;
return(restart):從新運行整個VCL,即從新從vcl_recv開始進行處理;每一次重啓都會增長req.restarts變量中的值,而max_restarts參數則用於限定最大重啓次數。
四、vcl_recv
vcl_recv是在Varnish完成對請求報文的解碼爲基本數據結構後第一個要執行的子例程,它一般有四個主要用途:
(1)修改客戶端數據以減小緩存對象差別性;好比刪除URL中的www.等字符;
(2)基於客戶端數據選用緩存策略;好比僅緩存特定的URL請求、不緩存POST請求等;
(3)爲某web應用程序執行URL重寫規則;
(4)挑選合適的後端Web服務器;
可使用下面的終止語句,即經過return()向Varnish返回的指示操做:
pass:繞過緩存,即不從緩存中查詢內容或不將內容存儲至緩存中;
pipe:不對客戶端進行檢查或作出任何操做,而是在客戶端與後端服務器之間創建專用「管道」,並直接將數據在兩者之間進行傳送;此時,keep-alive鏈接中後續傳送的數據也都將經過此管道進行直接傳送,並不會出如今任何日誌中;
lookup:在緩存中查找用戶請求的對象,若是緩存中沒有其請求的對象,後續操做極可能會將其請求的對象進行緩存;
error:由Varnish本身合成一個響應報文,通常是響應一個錯誤類信息、重定向類信息或負載均衡器返回的後端web服務器健康狀態檢查類信息;
vcl_recv也能夠經過精巧的策略完成必定意義上的安全功能,以將某些特定的***扼殺於搖籃中。同時,它也能夠檢查出一些拼寫類的錯誤並將其進行修正等。
Varnish默認的vcl_recv專門設計用來實現安全的緩存策略,它主要完成兩種功能:
(1)僅處理能夠識別的HTTP方法,而且只緩存GET和HEAD方法;
(2)不緩存任何用戶特有的數據;
安全起見,通常在自定義的vcl_recv中不要使用return()終止語句,而是再由默認vcl_recv進行處理,並由其作出相應的處理決策。
五、vcl_fetch
如前面所述,相對於vcl_recv是根據客戶端的請求做出緩存決策來講,vcl_fetch則是根據服務器端的響應做出緩存決策。在任何VCL狀態引擎中返回的pass操做都將由vcl_fetch進行後續處理。vcl_fetch中有許多可用的內置變量,好比最經常使用的用於定義某對象緩存時長的beresp.ttl變量。經過return()返回給arnish的操做指示有:
(1)deliver:緩存此對象,並將其發送給客戶端(經由vcl_deliver);
(2)hit_for_pass:不緩存此對象,但能夠致使後續對此對象的請求直接送達到vcl_pass進行處理;
(3)restart:重啓整個VCL,並增長重啓計數;超出max_restarts限定的最大重啓次數後將會返回錯誤信息;
(4)error code [reason]:返回指定的錯誤代碼給客戶端並丟棄此請求;
4、下面是varnish工做流程圖
5、實驗環境:
192.168.30.116 OS:Centos 6.4 x86_64 varnish.luojianlong.com
192.168.30.117 OS:Centos 6.4 x86_64 apache1.luojianlong.com
192.168.30.119 OS:Centos 6.4 x86_64 apache2.luojianlong.com
varnish version:varnish-3.0.5
安裝varnish
[root@varnish ~]# wget http://repo.varnish-cache.org/redhat/varnish-3.0/el6/noarch/varnish-release/varnish-release-3.0-1.el6.noarch.rpm [root@varnish ~]# rpm -ivh varnish-release-3.0-1.el6.noarch.rpm [root@varnish ~]# yum -y install varnish --enablerepo=varnish-3.0
調整varnish監聽端口爲80,存儲類型爲malloc
[root@varnish ~]# cp /etc/sysconfig/varnish /etc/sysconfig/varnish.bak [root@varnish ~]# vi /etc/sysconfig/varnish # 修改和添加以下幾項 VARNISH_LISTEN_PORT=6081 改成 VARNISH_LISTEN_PORT=80 添加 VARNISH_MEMORY_SIZE=64M VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}"改成 VARNISH_STORAGE="malloc,${VARNISH_MEMORY_SIZE}" [root@varnish ~]# service varnish start Starting Varnish Cache: [ OK ] [root@varnish ~]# ss -anplt | grep :80 LISTEN 0 128 :::80 :::* users:(("varnishd",19452,8)) LISTEN 0 128 *:80 *:* users:(("varnishd",19452,7)) [root@varnish ~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 200 ----------------------------- Varnish Cache CLI 1.0 ----------------------------- Linux,2.6.32-431.3.1.el6.x86_64,x86_64,-smalloc,-smalloc,-hcritbit varnish-3.0.5 revision 1a89b1f Type 'help' for command list. Type 'quit' to close CLI session. varnish> # 啓動正常
在倆臺後端服務器上安裝httpd,並創建測試文件
[root@apache1 ~]# yum -y install httpd [root@apache1 ~]# service httpd start [root@apache1 ~]# ss -anptl | grep :80 LISTEN 0 128 :::80 :::* users:(("httpd",18511,5),("httpd",18513,5),("httpd",18514,5),("httpd",18515,5),("httpd",18516,5),("httpd",18517,5),("httpd",18518,5),("httpd",18519,5),("httpd",18520,5)) [root@apache1 ~]# echo "apache1.luojianlong.com" > /var/www/html/index.html [root@apache1 ~]# curl http://192.168.30.117/index.html apache1.luojianlong.com #一樣在另一臺服務器上也執行上述操做,並創建測試頁面
自定義vcl配置文件
[root@varnish ~]# vi /etc/varnish/test.vcl backend websrv1 { .host = "192.168.30.117"; .port = "80"; } sub vcl_deliver { if (obj.hits > 0 ) { set resp.http.X-Cache = "HIT"; } else { set resp.http.X-Cache = "MISS"; } return (deliver); } [root@varnish ~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 200 ----------------------------- Varnish Cache CLI 1.0 ----------------------------- Linux,2.6.32-431.3.1.el6.x86_64,x86_64,-smalloc,-smalloc,-hcritbit varnish-3.0.5 revision 1a89b1f Type 'help' for command list. Type 'quit' to close CLI session. varnish> vcl.load test1 test.vcl 200 VCL compiled. varnish> vcl.use test1 200 varnish> vcl.list 200 available 0 boot active 2 test1 varnish>
使用curl命令,測試緩存是否生效
[root@apache2 ~]# curl -I http://192.168.30.116/index.html HTTP/1.1 200 OK Server: Apache/2.2.15 (CentOS) Last-Modified: Tue, 08 Apr 2014 07:34:38 GMT ETag: "1fef6-18-4f683043844cd" Content-Type: text/html; charset=UTF-8 Content-Length: 24 Accept-Ranges: bytes Date: Tue, 08 Apr 2014 08:46:20 GMT X-Varnish: 1291353379 1291353378 Age: 1 Via: 1.1 varnish Connection: keep-alive X-Cache: HIT [root@apache2 ~]# curl -I http://192.168.30.116/index.html HTTP/1.1 200 OK Server: Apache/2.2.15 (CentOS) Last-Modified: Tue, 08 Apr 2014 07:34:38 GMT ETag: "1fef6-18-4f683043844cd" Content-Type: text/html; charset=UTF-8 Content-Length: 24 Accept-Ranges: bytes Date: Tue, 08 Apr 2014 08:46:21 GMT X-Varnish: 1291353380 1291353378 Age: 2 Via: 1.1 varnish Connection: keep-alive X-Cache: HIT
發現緩存生效
若是想在客戶端知道是在哪臺服務器命中的緩存,則修改以下配置
[root@varnish ~]# vi /etc/varnish/test.vcl backend websrv1 { .host = "192.168.30.117"; .port = "80"; } sub vcl_deliver { if (obj.hits > 0 ) { set resp.http.X-Cache = "HIT from" + " " + server.ip; } else { set resp.http.X-Cache = "MISS"; } return (deliver); } varnish> vcl.load test2 test.vcl 200 VCL compiled. varnish> vcl.use test2 200 varnish> vcl.list 200 available 0 boot available 2 test1 active 0 test2 [root@apache2 ~]# curl -I http://192.168.30.116/index.html HTTP/1.1 200 OK Server: Apache/2.2.15 (CentOS) Last-Modified: Tue, 08 Apr 2014 07:34:38 GMT ETag: "1fef6-18-4f683043844cd" Content-Type: text/html; charset=UTF-8 Content-Length: 24 Accept-Ranges: bytes Date: Tue, 08 Apr 2014 08:51:23 GMT X-Varnish: 1291353381 Age: 0 Via: 1.1 varnish Connection: keep-alive X-Cache: MISS [root@apache2 ~]# curl -I http://192.168.30.116/index.html HTTP/1.1 200 OK Server: Apache/2.2.15 (CentOS) Last-Modified: Tue, 08 Apr 2014 07:34:38 GMT ETag: "1fef6-18-4f683043844cd" Content-Type: text/html; charset=UTF-8 Content-Length: 24 Accept-Ranges: bytes Date: Tue, 08 Apr 2014 08:51:24 GMT X-Varnish: 1291353382 1291353381 Age: 1 Via: 1.1 varnish Connection: keep-alive X-Cache: HIT from 192.168.30.116
發現已經顯現緩存命中的地址
若是想指定某個資源不使用緩存,而直接發日後端服務器,則修改以下配置
[root@varnish ~]# vi /etc/varnish/test.vcl backend websrv1 { .host = "192.168.30.117"; .port = "80"; } sub vcl_recv { if (req.url ~ "index.html$") { return (pass); } } sub vcl_deliver { if (obj.hits > 0 ) { set resp.http.X-Cache = "HIT from" + " " + server.ip; } else { set resp.http.X-Cache = "MISS"; } return (deliver); } varnish> vcl.load test3 test.vcl 200 VCL compiled. varnish> vcl.use test3 200 varnish> vcl.list 200 available 0 boot available 0 test1 available 2 test2 active 0 test3 [root@apache2 ~]# curl -I http://192.168.30.116/index.html HTTP/1.1 200 OK Server: Apache/2.2.15 (CentOS) Last-Modified: Tue, 08 Apr 2014 07:34:38 GMT ETag: "1fef6-18-4f683043844cd" Accept-Ranges: bytes Content-Length: 24 Content-Type: text/html; charset=UTF-8 Accept-Ranges: bytes Date: Tue, 08 Apr 2014 08:56:35 GMT X-Varnish: 1291353385 Age: 0 Via: 1.1 varnish Connection: keep-alive X-Cache: MISS [root@apache2 ~]# curl -I http://192.168.30.116/index.html HTTP/1.1 200 OK Server: Apache/2.2.15 (CentOS) Last-Modified: Tue, 08 Apr 2014 07:34:38 GMT ETag: "1fef6-18-4f683043844cd" Accept-Ranges: bytes Content-Length: 24 Content-Type: text/html; charset=UTF-8 Accept-Ranges: bytes Date: Tue, 08 Apr 2014 08:56:37 GMT X-Varnish: 1291353386 Age: 0 Via: 1.1 varnish Connection: keep-alive X-Cache: MISS
測試發現,不管怎麼訪問,index.html始終沒法命中緩存
強制緩存未命中:
在vcl_recv中使用return(pass)可以強制到上游服務器取得請求的內容,但這也會致使沒法將其緩存。使用purge會移除舊的緩存對象,但若是上游服務器宕機而沒法取得新版本的內容時,此內容將沒法再響應給客戶端。使用req.has_always_miss=ture,可讓Varnish在緩存中搜尋相應的內容但卻老是迴應「未命中」,因而vcl_miss將後續地負責啓動vcl_fetch從上游服務器取得新內容,並以新內容緩存覆蓋舊內容。此時,若是上游服務器宕機或未響應,舊的內容將保持原狀,並可以繼續服務於那些未使用req.has_always_miss=true的客戶端,直到其過時失效或由其它方法移除。
若是想在後端主機日誌中記錄用戶的IP信息,則能夠修改以下配置
[root@varnish ~]# vi /etc/varnish/test.vcl backend websrv1 { .host = "192.168.30.117"; .port = "80"; } sub vcl_recv { set req.http.X-Forward-For = client.ip; if (req.url ~ "index.html$") { return (pass); } } sub vcl_deliver { if (obj.hits > 0 ) { set resp.http.X-Cache = "HIT from" + " " + server.ip; } else { set resp.http.X-Cache = "MISS"; } return (deliver); } varnish> vcl.load test4 test.vcl 200 VCL compiled. varnish> vcl.use test4 200 varnish> vcl.list 200 available 0 boot available 0 test1 available 0 test2 available 2 test3 active 0 test4 # 登陸後端服務器,修改日誌格式 [root@apache1 ~]# vi /etc/httpd/conf/httpd.conf # 修改LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined 爲 LogFormat "%{X-Forward-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined [root@apache1 ~]# service httpd restart [root@apache2 ~]# curl -I http://192.168.30.116/index.html HTTP/1.1 200 OK Server: Apache/2.2.15 (CentOS) Last-Modified: Tue, 08 Apr 2014 07:34:38 GMT ETag: "1fef6-18-4f683043844cd" Accept-Ranges: bytes Content-Length: 24 Content-Type: text/html; charset=UTF-8 Accept-Ranges: bytes Date: Tue, 08 Apr 2014 09:08:00 GMT X-Varnish: 1291353391 Age: 0 Via: 1.1 varnish Connection: keep-alive X-Cache: MISS [root@apache1 ~]# tail -f /etc/httpd/logs/access_log 192.168.30.116 - - [08/Apr/2014:16:51:23 +0800] "GET /index.html HTTP/1.1" 200 24 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" 192.168.30.116 - - [08/Apr/2014:16:56:33 +0800] "HEAD /index.html HTTP/1.1" 200 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" 192.168.30.116 - - [08/Apr/2014:16:56:34 +0800] "HEAD /index.html HTTP/1.1" 200 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" 192.168.30.116 - - [08/Apr/2014:16:56:35 +0800] "HEAD /index.html HTTP/1.1" 200 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" 192.168.30.116 - - [08/Apr/2014:16:56:37 +0800] "HEAD /index.html HTTP/1.1" 200 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" - - - [08/Apr/2014:17:05:32 +0800] "HEAD /index.html HTTP/1.1" 200 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" - - - [08/Apr/2014:17:06:01 +0800] "HEAD /index.html HTTP/1.1" 200 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" - - - [08/Apr/2014:17:06:02 +0800] "HEAD /index.html HTTP/1.1" 200 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" - - - [08/Apr/2014:17:06:04 +0800] "HEAD /index.html HTTP/1.1" 200 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" 192.168.30.119 - - [08/Apr/2014:17:08:00 +0800] "HEAD /index.html HTTP/1.1" 200 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh
測試發現後端服務器日誌已經記錄了客戶端的IP地址
若是想自定義緩存的時間,則能夠修改以下配置
[root@varnish ~]# vi /etc/varnish/test.vcl backend websrv1 { .host = "192.168.30.117"; .port = "80"; } sub vcl_recv { set req.http.X-Forward-For = client.ip; if (req.url ~ "index.html$") { return (pass); } } sub vcl_fetch { if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg)$") { set beresp.ttl = 3600s; } } sub vcl_deliver { if (obj.hits > 0 ) { set resp.http.X-Cache = "HIT from" + " " + server.ip; } else { set resp.http.X-Cache = "MISS"; } return (deliver); }
若是想實現根據不一樣的請求內容發送不一樣的後端服務器,則修改以下配置
[root@varnish ~]# vi /etc/varnish/test.vcl backend websrv1 { .host = "192.168.30.117"; .port = "80"; } backend websrv2 { .host = "192.168.30.119"; .port = "80"; } sub vcl_recv { set req.http.X-Forward-For = client.ip; if (req.url ~ "index.html$") { return (pass); } if (req.url ~ "\.php$") { set req.backend = websrv2; } else { set req.backend = websrv1; } } sub vcl_fetch { if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg)$") { set beresp.ttl = 3600s; } } sub vcl_deliver { if (obj.hits > 0 ) { set resp.http.X-Cache = "HIT from" + " " + server.ip; } else { set resp.http.X-Cache = "MISS"; } return (deliver); } varnish> vcl.load test6 test.vcl 200 VCL compiled. varnish> vcl.use test6 200 varnish> vcl.list 200 available 0 boot available 0 test1 available 0 test2 available 1 test3 available 1 test4 available 0 test5 active 0 test6 # apache2上面安裝php,並創建測試頁面 [root@apache2 ~]# yum -y install php [root@apache2 ~]# vi /var/www/html/index.php <?php echo "this apache2 php server"; ?> [root@apache2 ~]# service httpd restart [root@apache2 ~]# curl http://192.168.30.116/index.php this apache2 php server [root@apache2 ~]# curl http://192.168.30.116/index.html apache1.luojianlong.com
發現已經分發到不一樣的後端服務器
若是想配置varnish服務器對後端服務器進行狀態檢測,則配置
[root@varnish ~]# vi /etc/varnish/test.vcl probe healthchk { .url = "/"; .interval = 5s; .timeout = 2s; } backend websrv1 { .host = "192.168.30.117"; .port = "80"; .probe = healthchk; } backend websrv2 { .host = "192.168.30.119"; .port = "80"; .probe = healthchk; } sub vcl_recv { set req.http.X-Forward-For = client.ip; if (req.url ~ "index.html$") { return (pass); } if (req.url ~ "\.php$") { set req.backend = websrv2; } else { set req.backend = websrv1; } } sub vcl_fetch { if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg)$") { set beresp.ttl = 3600s; } } sub vcl_deliver { if (obj.hits > 0 ) { set resp.http.X-Cache = "HIT from" + " " + server.ip; } else { set resp.http.X-Cache = "MISS"; } return (deliver); } varnish> vcl.load test8 test.vcl 200 VCL compiled. varnish> vcl.use test8 200 varnish> vcl.list 200 available 0 boot available 0 test1 available 0 test2 available 0 test3 available 0 test4 available 0 test5 available 0 test6 available 2 test7 active 0 test8
測試,將apache2服務器的httpd中止
[root@apache2 ~]# service httpd stop Stopping httpd: [ OK ]
打開瀏覽器訪問
發現狀態檢測已經生效
Varnish能夠檢測後端主機的健康狀態,在斷定後端主機失效時能自動將其從可用後端主機列表中移除,而一旦其從新變得可用還能夠自動將其設定爲可用。爲了不誤判,Varnish在探測後端主機的健康狀態發生轉變時(好比某次探測時某後端主機忽然成爲不可用狀態),一般須要連續執行幾回探測均爲新狀態纔將其標記爲轉換後的狀態。
每一個後端服務器當前探測的健康狀態探測方法經過.probe進行設定,其結果可由req.backend.healthy變量獲取,也可經過varnishlog中的Backend_health查看或varnishadm的debug.health查看。
.probe中的探測指令經常使用的有:
(1) .url:探測後端主機健康狀態時請求的URL,默認爲「/」;
(2) .request: 探測後端主機健康狀態時所請求內容的詳細格式,定義後,它會替換.url指定的探測方式;好比:
.request =
"GET /.healthtest.html HTTP/1.1"
"Host: www.baidu.com"
"Connection: close";
(3) .window:設定在斷定後端主機健康狀態時基於最近多少次的探測進行,默認是8;
(4) .threshold:在.window中指定的次數中,至少有多少次是成功的才斷定後端主機正健康運行;默認是3;
(5) .initial:Varnish啓動時對後端主機至少須要多少次的成功探測,默認同.threshold;
(6) .expected_response:指望後端主機響應的狀態碼,默認爲200;
(7) .interval:探測請求的發送週期,默認爲5秒;
(8) .timeout:每次探測請求的過時時長,默認爲2秒;
若是Varnish在某時刻沒有任何可用的後端主機,它將嘗試使用緩存對象的「寬容副本」(graced copy),固然,此時VCL中的各類規則依然有效。所以,更好的辦法是在VCL規則中判斷req.backend.healthy變量顯示某後端主機不可用時,爲此後端主機增大req.grace變量的值以設定適用的寬容期限長度。
若是varnish緩存後端服務器有多臺,對多個後端服務器作負載均衡,則要配置
# 調度算法使用hash [root@varnish ~]# vi /etc/varnish/test.vcl probe healthchk { .url = "/"; .interval = 5s; .timeout = 2s; } backend websrv1 { .host = "192.168.30.117"; .port = "80"; .probe = healthchk; } backend websrv2 { .host = "192.168.30.119"; .port = "80"; .probe = healthchk; } director webservers hash { { .backend = websrv1; .weight = 2; } { .backend = websrv2; .weight = 1; } } sub vcl_recv { set req.http.X-Forward-For = client.ip; if (req.url ~ "\.html|js|css|jpg|gif|png|jpeg$") { return (pass); } # if (req.url ~ "\.php$") { # set req.backend = websrv2; # } else { # set req.backend = websrv1; # } set req.backend = webservers; } sub vcl_fetch { if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg)$") { set beresp.ttl = 3600s; } } sub vcl_deliver { if (obj.hits > 0 ) { set resp.http.X-Cache = "HIT from" + " " + server.ip; } else { set resp.http.X-Cache = "MISS"; } return (deliver); } varnish> vcl.load test9 test.vcl 200 VCL compiled. varnish> vcl.use test9 200
測試發現同一個客戶端被定向到同一臺後端服務器
# 使用random調度算法 [root@varnish ~]# vim /etc/varnish/test.vcl probe healthchk { .url = "/"; .interval = 5s; .timeout = 2s; } backend websrv1 { .host = "192.168.30.117"; .port = "80"; .probe = healthchk; } backend websrv2 { .host = "192.168.30.119"; .port = "80"; .probe = healthchk; } director webservers random { .retries = 2; { .backend = websrv1; .weight = 2; } { .backend = websrv2; .weight = 1; } } sub vcl_recv { set req.http.X-Forward-For = client.ip; if (req.url ~ "\.html|js|css|jpg|gif|png|jpeg$") { return (pass); } # if (req.url ~ "\.php$") { # set req.backend = websrv2; # } else { # set req.backend = websrv1; # } set req.backend = webservers; } sub vcl_fetch { if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg)$") { set beresp.ttl = 3600s; } } sub vcl_deliver { if (obj.hits > 0 ) { set resp.http.X-Cache = "HIT from" + " " + server.ip; } else { set resp.http.X-Cache = "MISS"; } return (deliver); } varnish> vcl.load test10 test.vcl 200 VCL compiled. varnish> vcl.use test10 200
測試發現,請求被隨機發送到後端服務器
Varnish中可使用director指令將一個或多個近似的後端主機定義爲一個邏輯組,並能夠指定的調度方式(也叫挑選方法)來輪流將請求發送至這些主機上。不一樣的director可使用同一個後端主機,而某director也可使用「匿名」後端主機(在director中直接進行定義)。每一個director都必須有其專用名,且在定義後必須在VCL中進行調用,VCL中任何能夠指定後端主機的位置都可以按需將其替換爲調用某已定義的director。
Varnish的director支持的挑選方法中比較簡單的有round-robin和random兩種。其中,round-robin類型沒有任何參數,只須要爲其指定各後端主機便可,挑選方式爲「輪叫」,並在某後端主機故障時再也不將其視做挑選對象;random方法隨機從可用後端主機中進行挑選,每個後端主機都須要一個.weight參數以指定其權重,同時還能夠director級別使用.retires參數來設定查找一個健康後端主機時的嘗試次數。
Varnish 2.1.0後,random挑選方法又多了兩種變化形式client和hash。client類型的director使用client.identity做爲挑選因子,這意味着client.identity相同的請求都將被髮送至同一個後端主機。client.identity默認爲cliet.ip,但也能夠在VCL中將其修改成所須要的標識符。相似地,hash類型的director使用hash數據做爲挑選因子,這意味着對同一個URL的請求將被髮往同一個後端主機,其經常使用於多級緩存的場景中。然而,不管是client還hash,當其傾向於使用後端主機不可用時將會從新挑選新的後端其機。
另外還有一種稱做fallback的director,用於定義備用服務器,以下所示:
director b3 fallback {
{ .backend = www1; }
{ .backend = www2; } // will only be used if www1 is unhealthy.
{ .backend = www3; } // will only be used if both www1 and www2
// are unhealthy.
}
若是想實現手動清除某個資源的緩存,則要配置
[root@varnish ~]# vim /etc/varnish/test.vcl acl purgers { "127.0.0.1"; "192.168.30.0"/24; } probe healthchk { .url = "/"; .interval = 5s; .timeout = 2s; } backend websrv1 { .host = "192.168.30.117"; .port = "80"; .probe = healthchk; } backend websrv2 { .host = "192.168.30.119"; .port = "80"; .probe = healthchk; } director webservers random { .retries = 2; { .backend = websrv1; .weight = 2; } { .backend = websrv2; .weight = 1; } } sub vcl_recv { if (req.request == "PURGE") { if (!client.ip ~ purgers) { error 405 "Method not allowed"; } return(lookup); } set req.http.X-Forward-For = client.ip; if (req.url ~ "\.html|js|css|jpg|gif|png|jpeg$") { return (pass); } # if (req.url ~ "\.php$") { # set req.backend = websrv2; # } else { # set req.backend = websrv1; # } set req.backend = webservers; } 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 503 "Purged on a passed object"; } } sub vcl_fetch { if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg)$") { set beresp.ttl = 3600s; } } sub vcl_deliver { if (obj.hits > 0 ) { set resp.http.X-Cache = "HIT from" + " " + server.ip; } else { set resp.http.X-Cache = "MISS"; } return (deliver); } varnish> varnload test11 test.vcl 200 VCL compiled. varnish> vcl.use test11 200
測試清除緩存
[root@apache2 ~]# curl -I PURGE http://192.168.30.116/index.php curl: (6) Couldn't resolve host 'PURGE' HTTP/1.1 404 Not Found Server: Apache/2.2.15 (CentOS) Content-Type: text/html; charset=iso-8859-1 Content-Length: 287 Accept-Ranges: bytes Date: Tue, 08 Apr 2014 10:12:24 GMT X-Varnish: 1291353453 1291353450 Age: 15 Via: 1.1 varnish Connection: keep-alive X-Cache: HIT from 192.168.30.116 [root@apache2 ~]# curl -X PURGE http://192.168.30.116/index.php <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title>200 Purged.</title> </head> <body> <h1>Error 200 Purged.</h1> <p>Purged.</p> <h3>Guru Meditation:</h3> <p>XID: 1291353454</p> <hr> <p>Varnish cache server</p> </body> </html> [root@apache2 ~]# curl -I PURGE http://192.168.30.116/index.php curl: (6) Couldn't resolve host 'PURGE' HTTP/1.1 404 Not Found Server: Apache/2.2.15 (CentOS) Content-Type: text/html; charset=iso-8859-1 Content-Length: 287 Accept-Ranges: bytes Date: Tue, 08 Apr 2014 10:13:13 GMT X-Varnish: 1291353455 Age: 0 Via: 1.1 varnish Connection: keep-alive X-Cache: MISS
發現緩存已經被清除
purge用於清理緩存中的某特定對象及其變種(variants),所以,在有着明確要修剪的緩存對象時可使用此種方式。HTTP協議的PURGE方法能夠實現purge功能,不過,其僅能用於vcl_hit和vcl_miss中,它會釋放內存工做並移除指定緩存對象的全部Vary:-變種,並等待下一個針對此內容的客戶端請求到達時刷新此內容。另外,其通常要與return(restart)一塊兒使用。
下面配置varnish的防盜鏈
[root@varnish ~]# vim /etc/varnish/test.vcl acl purgers { "127.0.0.1"; "192.168.30.0"/24; } probe healthchk { .url = "/"; .interval = 5s; .timeout = 2s; } backend websrv1 { .host = "192.168.30.117"; .port = "80"; .probe = healthchk; } backend websrv2 { .host = "192.168.30.119"; .port = "80"; .probe = healthchk; } director webservers random { .retries = 2; { .backend = websrv1; .weight = 2; } { .backend = websrv2; .weight = 1; } } sub vcl_recv { if (req.http.referer ~ "http://.*") { if (!(req.http.referer ~ "http://.*\.baidu\.com"|| req.http.referer ~ "http://.*\.google\.com.*")) { set req.http.host = "www.baidu.com"; set req.url = "/abc/abc.jpg"; } } if (req.request == "PURGE") { if (!client.ip ~ purgers) { error 405 "Method not allowed"; } return(lookup); } set req.http.X-Forward-For = client.ip; if (req.url ~ "\.html|js|css|jpg|gif|png|jpeg$") { return (pass); } # if (req.url ~ "\.php$") { # set req.backend = websrv2; # } else { # set req.backend = websrv1; # } set req.backend = webservers; } 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 503 "Purged on a passed object"; } } sub vcl_fetch { if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg)$") { set beresp.ttl = 3600s; } } sub vcl_deliver { if (obj.hits > 0 ) { set resp.http.X-Cache = "HIT from" + " " + server.ip; } else { set resp.http.X-Cache = "MISS"; } return (deliver); } varnish> vcl.load test12 test.vcl 200 VCL compiled. varnish> vcl.use test12 200
在apache1上安裝php,並在apahce1和apache2網站目錄裏分別創建/abc/abc.jpg拒絕提示頁面
[root@apache1 ~]# yum -y install php [root@apache1 ~]# service httpd restart [root@apache1 ~]# mkdir /var/www/html/abc [root@apache1 ~]# echo "not allow" >> /var/www/html/abc/abc.jpg [root@apache2 ~]# mkdir /var/www/html/abc [root@apache2 ~]# echo "not allow" >> /var/www/html/abc/abc.jpg
測試防盜鏈效果
[root@apache2 ~]# curl -e http://www.abc.com http://192.168.30.116/index.php not allow [root@apache2 ~]# curl -e http://www.baidu.com http://192.168.30.116/index.php this apache1 php server
測試發現防盜鏈效果生效
到此,varnish各類配置功能就說這麼多了。