一、varnish原理:
1)Varnish簡介:
varnish緩存是web應用加速器,同時也作為http反向緩存代理。你可以安裝varnish在任何http的前端,同時配置它緩存內容。與傳統的 squid 相比,varnish 具有性能更高、速度更快、管理更加方便等諸多優點。有一部分企業已經在生產環境中使用其作為舊版本的squid的替代方案,以在相同的服務器成本下提供更好的緩存效果,Varnish更是作為CDN緩存服務器的可選服務之一。
根據官網的介紹,Varnish的主要特性如下:https://www.varnish-cache.org/
1.緩存位置:可以使用內存也可以使用磁盤。如果要使用磁盤的話推薦SSD做RAID1
2.日志存儲:日志也存儲在內存中。存儲策略:固定大小,循環使用
3.支持虛擬內存的使用。
4.有精確的時間管理機制,即緩存的時間屬性控制。
5.狀態引擎架構:在不同的引擎上完成對不同的緩存和代理數據進行處理。可以通過特定的配置語言設計不同的控制語句,以決定數據在不同位置以不同方式緩存,在特定的地方對經過的報文進行特定規則的處理。
6.緩存管理:以二叉堆格式管理緩存數據,做到數據的及時清理。
2)Varnish與Squid的對比
相同點:
都是一個反向代理服務器;
都是開源軟件;
Varnish的優勢:
1、Varnish的穩定性很高,兩者在完成相同負荷的工作時,Squid服務器發生故障的幾率要高于Varnish,因為使用Squid要經常重啟;
2、Varnish訪問速度更快,因為采用了“Visual Page Cache”技術,所有緩存數據都直接從內存讀取,而squid是從硬盤讀取,因而Varnish在訪問速度方面會更快;
3、Varnish可以支持更多的并發連接,因為Varnish的TCP連接釋放要比Squid快,因而在高并發連接情況下可以支持更多TCP連接;
4、Varnish可以通過管理端口,使用正則表達式批量的清除部分緩存,而Squid是做不到的;
squid屬于是單進程使用單核CPU,但Varnish是通過fork形式打開多進程來做處理,所以可以合理的使用所有核來處理相應的請求;
Varnish的劣勢:
1、varnish進程一旦Crash或者重啟,緩存數據都會從內存中完全釋放,此時所有請求都會發送到后端服務器,在高并發情況下,會給后端服務器造成很大壓力;
2、在varnish使用中如果單個url的請求通過HA/F5等負載均衡,則每次請求落在不同的varnish服務器中,造成請求都會被穿透到后端;而且同樣的請求在多臺服務器上緩存,也會造成varnish的緩存的資源浪費,造成性能下降;
Varnish劣勢的解決方案:
針對劣勢一:在訪問量很大的情況下推薦使用varnish的內存緩存方式啟動,而且后面需要跟多臺squid/nginx服務器。主要為了防止前面的varnish服 務、服務器被重啟的情況下,大量請求穿透varnish,這樣squid/nginx可以就擔當第二層CACHE,而且也彌補了varnish緩存在內存中重啟都會釋放的問題;
針對劣勢二:可以在負載均衡上做url哈希,讓單個url請求固定請求到一臺varnish服務器上;
3)使用varnish作為web代理緩存的原理?:
varnish是一個http反向代理的緩存。它從客戶端接收請求然后嘗試從緩存中獲取數據來響應客戶端的請求,如果varnish不能從緩存中獲得數據來響應客戶端,它將轉發請求到后端(backend?servers),獲取響應同時存儲,最后交付給客戶端。?
如果varnish已經緩存了某個響應,它比你傳統的后端服務器的響應要快很多,所以你需要盡可能是更多的請求直接從varnish的緩存中獲取響應。?
varnish決定是緩存內容或者是從后端服務器獲取響應。后端服務器能通過http響應頭中的Cache-Control來同步varnish緩存內容。在某些條件下varnish將不緩存內容,最常見的是使用cookie。當一個被標記有cookie的客戶端web請求,varnish默認是不緩存。這些眾多的varnish功能特點都是可以通過寫vcl來改變的。
5)簡單架構:
Varnish 分為 management 進程和child 進程;
Management進程:對子進程進行管理,同時對VCL配置進行編譯,并應用到不同的狀態引擎。
Child進程:生成線程池,負責對用戶請求進行處理,并通過hash查找返回用戶結果。
6)varnish主要配置部分:
varnish配置主要分為:后端配置,ACL配置,probes配置,directors配置,核心子程序配置幾大塊。其中后端配置是必要的,在多臺服務器中還會用到directors配置,核心子程序配置。
后端配置:即給varnish添加反代服務器節點,最少配置一個。
ACL配置:即給varnish添加訪問控制列表,可以指定這些列表訪問或禁止訪問。
probes配置:即給varnish添加探測后端服務器是否正常的規則,方便切換或禁止對應后端服務器。
directors配置:即給varnish添加負載均衡模式管理多個后端服務器。
核心子程序配置:即給varnish添加后端服務器切換,請求緩存,訪問控制,錯誤處理等規則。
7)VCL中內置預設變量:變量(也叫object):
req:The request object,請求到達時可用的變量(客戶端發送的請求對象)
bereq:The backend request object,向后端主機請求時可用的變量
beresp:The backend response object,從后端主機獲取內容時可用的變量(后端響應請求對象)
resp:The HTTP response object,對客戶端響應時可用的變量(返回給客戶端的響應對象)
obj:存儲在內存中時對象屬性相關的可用的變量(高速緩存對象,緩存后端響應請求內容)
?
預設變量是系統固定的,請求進入對應的vcl子程序后便生成,這些變量可以方便子程序提取,當然也可以自定義一些全局變量。
當前時間:
now?:作用:返回當前時間戳。
?
客戶端:(客戶端基本信息)
client.ip:返回客戶端IP地址。
注:原client.port已經棄用,如果要取客戶端請求端口號使用 std.port(client.ip),需要import std;才可以使用std
client.identity:用于裝載客戶端標識碼。
?
服務器:(服務器基本信息)
注:原server.port已經棄用,如果要取服務器端口號使用 std.port(server.ip),需要import std;才可以使用std
server.hostname:服務器主機名。
server.identity:服務器身份標識。
server.ip:返回服務器端IP地址。
?
req :(客戶端發送的請求對象)
req:整個HTTP請求數據結構
req.backend_hint:指定請求后端節點,設置后 bereq.backend 才能獲取后端節點配置數據
req.can_gzip:客戶端是否接受GZIP傳輸編碼。
req.hash_always_miss:是否強制不命中高速緩存,如果設置為true,則高速緩存不會命中,一直會從后端獲取新數據。
req.hash_ignore_busy:忽略緩存中忙碌的對象,多臺緩存時可以避免死鎖。
req.http:對應請求HTTP的header。
req.method:請求類型(如 GET , POST)。
req.proto:客戶端使用的HTTP協議版本。
req.restarts:重新啟動次數。默認最大值是4
req.ttl:緩存有剩余時間。
req.url:請求的URL。
req.xid:唯一ID。
?
bereq:(發送到后端的請求對象,基于req對象)
bereq:整個后端請求后數據結構。
bereq.backend:所請求后端節點配置。
bereq.between_bytes_timeout:從后端每接收一個字節之間的等待時間(秒)。
bereq.connect_timeout:連接后端等待時間(秒),最大等待時間。
bereq.first_byte_timeout:等待后端第一個字節時間(秒),最大等待時間。
bereq.http:對應發送到后端HTTP的header信息。
bereq.method:發送到后端的請求類型(如:GET , POST)。
bereq.proto:發送到后端的請求的HTTP版本。
bereq.retries:相同請求重試計數。
bereq.uncacheable:無緩存這個請求。
bereq.url:發送到后端請求的URL。
bereq.xid:請求唯一ID。
?
beresp:(后端響應請求對象)
beresp:整個后端響應HTTP數據結構。
beresp.backend.ip:后端響應的IP。
beresp.backend.name:響應后端配置節點的name。
beresp.do_gunzip:默認為 false 。緩存前解壓該對象
beresp.do_gzip:默認為 false 。緩存前壓縮該對象
beresp.grace:設置當前對象緩存過期后可額外寬限時間,用于特殊請求加大緩存時間,當并發量巨大時,不易設置過大否則會堵塞緩存,一般可設置 1m 左右,當beresp.ttl=0s時該值無效。
beresp.http:對應的HTTP請求header
beresp.keep:對象緩存后帶保持時間
beresp.proto:響應的HTTP版本
beresp.reason:由服務器返回的HTTP狀態信息
beresp.status:由服務器返回的狀態碼
beresp.storage_hint:指定保存的特定存儲器
beresp.ttl:該對象緩存的剩余時間,指定統一緩存剩余時間。
beresp.uncacheable:繼承bereq.uncacheable,是否不緩存
?
OBJ :(高速緩存對象,緩存后端響應請求內容)
obj.grace:該對象額外寬限時間
obj.hits:緩存命中次數,計數器從1開始,當對象緩存該值為1,一般可以用于判斷是否有緩存,當前該值大于0時則為有緩存。
obj.http:對應HTTP的header
obj.proto:HTTP版本
obj.reason:服務器返回的HTTP狀態信息
obj.status:服務器返回的狀態碼
obj.ttl:該對象緩存剩余時間(秒)
obj.uncacheable:不緩存對象
?
resp :(返回給客戶端的響應對象)
resp:整個響應HTTP數據結構。
resp.http:對應HTTP的header。
resp.proto:編輯響應的HTTP協議版本。
resp.reason:將要返回的HTTP狀態信息。
resq.status:將要返回的HTTP狀態碼。
?
存儲 :
storage.<name>.free_space:存儲可用空間(字節數)。
storage.<name>.used_space:存儲已經使用空間(字節數)。
storage.<name>.happy:存儲健康狀態。
?
8、特定功能性語句
ban(expression):清除指定對象緩存
call(subroutine):調用子程序,如:call(name);
hash_data(input):生成hash鍵,用于制定hash鍵值生成結構,只能在vcl_hash子程序中使用。調用 hash_data(input) 后,即這個hash為當前頁面的緩存hash鍵值,無需其它獲取或操作,如:
sub?vcl_hash{
???????hash_data(client.ip);
???????return(lookup);
}
注意:return(lookup); 是默認返回值,所以可以不寫。
new():創建一個vcl對象,只能在vcl_init子程序中使用。
return():結束當前子程序,并指定繼續下一步動作,如:return (ok); 每個子程序可指定的動作均有不同。
rollback():恢復HTTP頭到原來狀態,已經棄用,使用 std.rollback() 代替。
synthetic(STRING):合成器,用于自定義一個響應內容,比如當請求出錯時,可以返回自定義 404 內容,而不只是默認頭信息,只能在 vcl_synth 與 vcl_backend_error 子程序中使用,如:
sub?vcl_synth?{
????//自定義內容
????synthetic?({"
<!DOCTYPE?HTML?PUBLIC?"-//W3C//DTD?HTML?4.01?Transitional//EN">
<html?lang="zh-cn">
???<head>
???<meta?http-equiv="Content-Type"?content="text/html;?charset=utf-8"/>
??????<title>error</title>
???</head>
???<body>
??????<h1>Error</h1>
??????<h3>這只是一個測試自定義響應異常內容</h3>
???</body>
</html>
????"});
????//只交付自定義內容
????return(deliver);
?
regsub(str, regex, sub):使用正則替換第一次出現的字符串,第一個參數為待處理字符串,第二個參數為正則表達式,第三個為替換為字符串。
regsuball(str, regex, sub):使用正則替換所有匹配字符串。參數與regsuball相同。
具體變量詳見:
https://www.varnish-cache.org/docs/4.0/reference/vcl.html#reference-vcl
?
(9)return 語句:
return 語句是終止子程序并返回動作,所有動作都根據不同的vcl子程序限定來選用。
https://www.varnish-cache.org/docs/4.0/users-guide/vcl-built-in-subs.html
語法:return(action);
常用的動作:
abandon? 放棄處理,并生成一個錯誤。
deliver? 交付處理
fetch? 從后端取出響應對象
hash? 哈希緩存處理
lookup?查找緩存對象
ok?繼續執行
pass? 進入pass非緩存模式
pipe?進入pipe非緩存模式
purge?清除緩存對象,構建響應
restart?重新開始
retry?重試后端處理
synth(status code,reason)?合成返回客戶端狀態信息
?
10)varnish中內置子程序有:
注:varnish內置子程序均有自己限定的返回動作??return (動作);? 不同的動作將調用對應下一個子程序。
vcl_recv子程序:
開始處理請求,通過?return (動作);?選擇varnish處理模式,默認進入hash緩存模式(即return(hash);),緩存時間為配置項default_ttl(默認為 120秒)過期保持時間default_grace(默認為10秒)。該子程序一般用于模式選擇,請求對象緩存及信息修改,后端節點修改,終止請求等操作。
可操作對象:(部分或全部值)
讀:client,server,req,storage
寫:client,req
返回值:
synth(status code,reason);? 定義響應內容。
pass? 進入pass模式,并進入vcl_pass子程序。
pipe? 進入pipe模式,并進入vcl_pipe子程序。
hash? 進入hash緩存模式,并進入vcl_hash子程序,默認返回值。
purge? 清除緩存等數據,子程序先從vcl_hash再到vcl_purge。
?
vcl_pipe子程序:
pipe模式處理,該模式主要用于直接取后端響應內容返回客戶端,可定義響應內容返回客戶端。該子程序一般用于需要及時且不作處理的后端信息,取出后端響應內容后直接交付到客戶端不進入vcl_deliver子程序處理。
可操作對象:(部分或全部值)
讀:client,server,bereq,req,storage
寫:client,bereq,req
返回值:
synth(status code,reason);? 定義響應內容。
pipe? 繼續pipe模式,進入后端vcl_backend_fetch子程序,默認返回值。
?
vcl_pass子程序:
pass模式處理,該模式類似hash緩存模式,僅不做緩存處理。
可操作對象:(部分或全部值)
讀:client,server,req,storage
寫:client,req
返回值:
synth(status code,reason);? 定義響應內容。
fetch? 繼續pass模式,進入后端vcl_backend_fetch子程序,默認返回值。
?
vcl_hit子程序:
hash緩存模式時,存在hash緩存時調用,用于緩存處理,可放棄或修改緩存。
可操作對象:(部分或全部值)
讀:client,server,obj,req,storage
寫:client,req
返回值:
restart?重啟請求。
deliver?交付緩存內容,進入vcl_deliver子程序處理,默認返回值。
synth(status code,reason);? 定義響應內容。
?
vcl_miss子程序:
hash緩存模式時,不存在hash緩存時調用,用于判斷性的選擇進入后端取響應內容,可以修改為pass模式。
可操作對象:(部分或全部值)
讀:client,server,req,storage
寫:client,req
返回值:
restart?重啟請求。
synth(status code,reason);? 定義響應內容。
pass?切換到pass模式,進入vcl_pass子程序。
fetch? 正常取后端內容再緩存,進入vcl_backend_fetch子程序,默認返回值。
?
vcl_hash子程序:
hash緩存模式,生成hash值作為緩存查找鍵名提取緩存內容,主要用于緩存hash鍵值處理,可使用hash_data(string) 指定鍵值組成結構,可在同一個頁面通過IP或cookie生成不同的緩存鍵值。
可操作對象:(部分或全部值)
讀:client,server,req,storage
寫:client,req
返回值:
lookup?查找緩存對象,存在緩存進入vcl_hit子程序,不存在緩存進入vcl_miss子程序,當使用了purge清理模式時會進入vcl_purge子程序,默認返回值。
?
vcl_purge子程序:
清理模式,當查找到對應的緩存時清除并調用,用于請求方法清除緩存,并報告。
可操作對象:(部分或全部值)
讀:client,server,req,storage
寫:client,req
返回值:
synth(status code,reason);? 定義響應內容。
restart?重啟請求。
?
vcl_deliver子程序:
客戶端交付子程序,在vcl_backend_response子程序后調用(非pipe模式),或vcl_hit子程序后調用,可用于追加響應頭信息,cookie等內容。
可操作對象:(部分或全部值)
讀:client,server,req,resp,obj,storage
寫:client,req,resp
返回值:
deliver?正常交付后端或緩存響應內容,默認返回值。
restart?重啟請求。
?
vcl_backend_fetch子程序:
發送后端請求之前調用,可用于改變請求地址或其它信息,或放棄請求。
可操作對象:(部分或全部值)
讀:server,bereq,storage
寫:bereq
返回值:
fetch?正常發送請求到到后端取出響應內容,進入vcl_backend_response子程序,默認返回值。
abandon?放棄后端請求,并生成一個錯誤,進入vcl_backend_error子程序。
?
vcl_backend_response子程序:
后端響應后調用,可用于修改緩存時間及緩存相關信息。
可操作對象:(部分或全部值)
讀:server,bereq,beresp,storage
寫:bereq,beresp
返回值:
deliver?正常交付后端響應內容,進入vcl_deliver子程序,默認返回值。
abandon?放棄后端請求,并生成一個錯誤,進入vcl_backend_error子程序。
retry?重試后端請求,重試計數器加1,當超過配置中max_retries值時會報錯并進入vcl_backend_error子程序。
?
vcl_backend_error子程序:
后端處理失敗調用,異常頁面展示效果處理,可自定義錯誤響應內容,或修改beresp.status與beresp.http.Location重定向等。
可操作對象:(部分或全部值)
讀:server,bereq,beresp,storage
寫:bereq,beresp
返回值:
deliver?只交付 sysnthetic(string) 自定義內容,默認返回后端異常標準錯誤內容。
retry?重試后端請求,重試計數器加1,當超過配置中max_retries值時會報錯并進入vcl_backend_error子程序。
?
vcl_synth子程序:
自定義響應內容。可以通過synthetic()和返回值 synth 調用,這里可以自定義異常顯示內容,也可以修改resp.status與resp.http.Location重定向。
可操作對象:(部分或全部值)
讀:client,server,req,resp,storage
寫:req,resp
返回值:
deliver?只交付 sysnthetic(string) 自定義內容,默認返回 sysnth 異常指定狀態碼與錯誤內容。
restart?重啟請求。
?
vcl_init子程序:
加載vcl時最先調用,用于初始化VMODs,該子程序不參與請求處理,僅在vcl加載時調用一次。
可操作對象:(部分或全部值)
讀:server
寫:無
返回值:
ok?正常返回,進入vcl_recv子程序,默認返回值。
?
vcl_fini子程序:
卸載當前vcl配置時調用,用于清理VMODs,該子程序不參與請求處理,僅在vcl正常丟棄后調用。
可操作對象:(部分或全部值)
讀:server
寫:無
返回值:
ok?正常返回,本次vcl將釋放,默認返回值。
varnish子程序調用流程圖,通過大部分子程序的return返回值進入下一步行動:
11)優雅模式(Garce mode)
Varnish中的請求合并
當幾個客戶端請求同一個頁面的時候,varnish只發送一個請求到后端服務器,然后讓其他幾個請求掛起并等待返回結果;獲得結果后,其它請求再復制后端的結果發送給客戶端;
但如果同時有數以千計的請求,那么這個等待隊列將變得龐大,這將導致2類潛在問題:
驚群問題(thundering herd problem),即突然釋放大量的線程去復制后端返回的結果,將導致負載急速上升;沒有用戶喜歡等待;
故為了解決這類問題,可以配置varnish在緩存對象因超時失效后再保留一段時間,以給那些等待的請求返回過去的文件內容(stale content),配置案例如下:
sub vcl_recv {
if (! req.backend.healthy) {
set req.grace = 5m;
} else {
set req.grace = 15s;
}
}
sub vcl_fetch {
set beresp.grace = 30m;
}
以上配置表示varnish將會將失效的緩存對象再多保留30分鐘,此值等于最大的req.grace值即可;
而根據后端主機的健康狀況,varnish可向前端請求分別提供5分鐘內或15秒內的過期內容
二、安裝varnish
1、安裝依賴關系的軟件包(注:使用centos在線yum源)
[root@varnish ~]# yum -y install autoconf automake libedit-devel libtool ncurses-devel pcre-devel pkgconfig python-docutils python-sphinx
2、安裝varnish
Varnish的官方網址為http://varnish-cache.org,可以在這里下載最新版本的軟件。
下載地址:https://www.varnish-cache.org/content/varnish-cache-403
注意:Varnish網站有時會被墻。
Git 下載:git clone https://github.com/varnish/Varnish-Cache /var/tmp/
解壓,進入解壓目錄編譯安裝:
[root@varnish ~]# tar zxf varnish-4.0.3.tar.gz
[root@varnish ~]# cd varnish-4.0.3/
[root@varnish varnish-4.0.3]# export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
注:
./autogen.sh?
如果從Git庫下載的安裝包時才需要運行,用于生成configure編譯文件。
配置:
[root@varnish varnish-4.0.3]# ./configure
注:不指定安裝路徑,默認是安裝在/usr/local目錄下
編譯、安裝
[root@varnish varnish-4.0.3]# make && make install
復制 vcl 文件(在編譯安裝目錄下),如果安裝目錄里沒有 default.vcl 文件。
復制到安裝目錄的/usr/local/var/varnish/目錄下(當然并無必需要求在哪個目錄,因為正式啟動時還得指定這個文件的目錄)
[root@varnish varnish-4.0.3]# cp etc/example.vcl ?/usr/local/var/varnish/default.vcl
三、varnish實例解析:
varnish 配置基本上是編輯 VCL(Varnish?Configuration?Language) 文件,varnish 有一套自定義 VCL 語法,啟動時,會將配置文件編譯為C語言,再執行。
varnish 4.0開始,每個VCL文件必須在開始行聲明它的版本“vcl 4.0;”
塊(子程序)由大括號分隔,語句用分號結束。所有的關鍵字及預設子程序名都是全小寫。
注釋:支持 // 或 #? 多行時還可以使用 /* .. */
1、后端服務器地址池配置及后端服務器健康檢查
varnish有"后端"或者"源"服務器的概念。backend server提供給varnish加速的內容。實際上就是給varnish添加可供訪問的web服務器,如果有多臺web服務器時,可添加多個backend塊。
1)后端服務器定義:
命令:backend。這個定義為最基本的反向入口定義,用于varnish連接對應的服務器,如果沒有定義或定義錯誤則用戶無法訪問正常頁面。
語法格式:
backend?name{
????.attribute?=?"value";
}
說明:backend?是定義后端關鍵字,name?是當前后端節點的別名,多個后端節點時,name 名不能重復,否則覆蓋。花括號里面定義當前節點相關的屬性(鍵=值)。除默認節點外其它節點定義后必需有調用,否則varnish無法啟動。后端是否正常可以通過std.healthy(backend)判斷。
支持運算符:
=?? (賦值運算)
==? (相等比較)
~??? (匹配,可以使用正則表達式,或訪問控制列表)
!~??? (不匹配,可以使用正則表達式,或訪問控制列表)
!? (非)
&&?? (邏輯與)
||?? (邏輯或)
屬性列表:
.host="xxx.xxx.xxx.xxx";????? //要轉向主機(即后端主機)的IP或域名,必填鍵/值對。
.port="8080";??????? //主機連接端口號或協議名(HTTP等),默認80
.host_header='';??? //請示主機頭追加內容
.connect_timeout=1s;???? //連接后端的超時時間
.first_byte_timeout=5s;??? //等待從后端返回的第一個字節時間
.between_bytes_timeout=2s;???? //每接收一個字節之間等待時間
.probe=probe_name;??????? //監控后端主機的狀態,指定外部監控name或者內部直接添加
.max_connections=200;??? //設置最大并發連接數,超過這個數后連接就會失敗
例:(下面兩個例子結果是一樣的,但第二個例子中更適用于集群,可以方便批量修改)
backend?web{
????.host="192.168.31.83";
????.port="80";
????.probe={??????????//直接追加監控塊.probe是一個的參數
????????.url="/";
????????.timeout=2s;
????}
}
或
probe?web_probe{???//監控必需定義在前面,否則后端調用找不到監控塊。
????.url="/";
????.timeout=2s;
}
?
backend?web{
????.host="192.168.31.83";
????.port="80";
????.probe=web_probe;???//調用外部共用監控塊
}
?
2)監視器定義:
命令:probe 。監控可以循環訪問指定的地址,通過響應時間判定服務器是否空閑或正常。這類命令非常適用于集群中某些節點服務器崩潰或負載過重,而禁止訪問這臺節點服務器。
語法格式:
probe?name{
????.attribute?=?"value";
}
說明:probe?是定義監控關鍵字,name?是當前監控點的別名,多個監控節點時,name 名不能重復,否則覆蓋。花括號里面定義當前節點相關的屬性(鍵=值)。
沒有必填屬性,因為默認值就可以正常執行操作。
屬性列表:
.url="/";??? //指定監控入口URL地址,默認為"/"
.request="";?? //指定監控請求入口地址,比 .url 優先級高。
.expected_response="200";?? //請求響應代碼,默認是 200
.timeout=2s;?? //請求超時時間。
.interval=5s;???? //每次輪詢請求間隔時間,默認為 5s 。
.initial=-1;???? //初始啟動時以.window輪詢次數中幾次良好后續才能使用這個后端服務器節點,默認為 -1 ,則輪詢完 .window 所有次數良好判定為正常。
.window=8;?? //指定多少輪詢次數,用于判定服務器正常,默認是 8。
.threshold=3;?? //必須多少次輪詢正常才算該后端節點服務器正常,默認是 3。
例:創建健康監測,定義健康檢查名稱為backend_healthcheck
probe?backend_healthcheck?{
????????.url?=?"/";
????????.timeout?=?1s;
????????.interval?=?5s;
????????.window?=?5;
????????.threshold?=?3;
????}
在上面的例子中varnish將每5s檢測后端,超時設為1s。每個檢測將會發送get /的請求。如果5個檢測中大于3個是成功,varnish就認為后端是健康的,反之,后端就有問題了。
?
3)集群負載均衡directors:
varnish可以定義多個后端,也可以將幾個后端放在一個后端集群里面已達到負載均衡的目的。
你也可以將幾個后端組成一組后端。這個組被叫做Directors。可以提高性能和彈性。
directors是varnish負載均衡模塊,使用前必需引入directors模塊,directors模塊主要包含:round_robin,random,hash,fallback負載均衡模式。
round_robin?: 循環依次逐個選擇后端服務器。
random?: 隨機選擇后端服務器,可設置每個后端權重增加隨機率。
hash?:? 通過散列隨機選擇對應的后端服務器且保持選擇對應關系,下次則直接找對應的后端服務器。
Fallback:后備
注意:random,hash 有權重值設置,用于提高隨機率。每個后端最好都配置監控器(后端服務器正常監測)以便directors自動屏蔽不正常后端而不進入均衡列隊中。
這些操作需要你載入VMOD(varnish module),然后在vcl_init中調用這個VMOD。
import?directors;???? ? ? ? ? ? ?#?load?the?directors
backend?web1?{
.host?=?"192.168.0.10";
.port = "80";
.probe = backend_healthcheck;
}
backend?web2?{
.host?=?"192.168.0.11";
.port = "80";
.probe = backend_healthcheck;
}
//初始化處理
sub?vcl_init?{ ? ? ? ? ? ?//調用vcl_init初始化子程序創建后端主機組,即directors
????new ?web_cluster?=?directors.round_robin(); //使用new關鍵字創建drector對象,使用round_robin算法
????web_cluster.add_backend(web1); ? //添加后端服務器節點
web_cluster.add_backend(web2);
}
//開始處理請求
sub?vcl_recv?{ ? ? ? ? ? ? ? ? ? ? //調用vcl_recv子程序,用于接收和處理請求
????set ?req.backend_hint?=?web_cluster.backend(); ? ? //選取后端
}
說明:
set命令是設置變量
unset命令是刪除變量
web_cluster.add_backend( backend , real );? 添加后端服務器節點,backend 為后端配置別名,real 為權重值,隨機率計算公式:100 * (當前權重 / 總權重)。
req.backend_hint是varnish的預定義變量,作用是指定請求后端節點
vcl對象需要使用new關鍵字創建,所有可創建對象都是內定的,使用前必需import,所有new操作只能在vcl_init子程序中。
擴展:varnish將不同的url發送到不同的后端server
import?directors;???? ? ? ? ? ? ?#?load?the?directors
backend?web1?{
.host?=?"192.168.0.10";
.port = "80";
.probe = backend_healthcheck;
}
backend?web2?{
.host?=?"192.168.0.11";
.port = "80";
.probe = backend_healthcheck;
}
backend img1 {
????????.host = "img1.lnmmp.com";
????????.port = "80";
????????.probe = backend_healthcheck;
}
backend img2 {
????????.host = "img2.lnmmp.com";
????????.port = "80";
????????.probe = backend_healthcheck;
}
//初始化處理
sub?vcl_init?{ ? ? ? ? ? ?//調用vcl_init初始化子程序創建后端主機組,即directors
????new ?web_cluster?=?directors.round_robin(); //使用new關鍵字創建drector對象,使用round_robin算法
????web_cluster.add_backend(web1); ? //添加后端服務器節點
web_cluster.add_backend(web2);
new img_cluster = directors.random();
img_cluster.add_backend(img1,2); ? //添加后端服務器節點,并且設置權重值
img_cluster.add_backend(img2,5);
}
//根據不同的訪問域名,分發至不同的后端主機組
sub?vcl_recv?{
if (req.http.host ~ "(?i)^(www.)?benet.com$") {
????????????????set ?req.http.host = "www.benet.com";
????????????????set ?req.backend_hint = web_cluster.backend(); ?//選取后端
????????} elsif (req.http.host ?~ ?"(?i)^p_w_picpaths.benet.com$") {
????????? ? set ?req.backend_hint = img_cluster.backend();
????????}
}
說明:中的i就是忽略大小寫的意思。(?i)表示開啟忽略大小寫,而(?-i)表示關閉忽略大小寫
?
4)訪問控制列表(ACL):
創建一個地址列表,用于后面的判斷,可以是域名或IP集合。這個可以用于指定某些地址請求入口,防止惡意請求等。
語法格式:
acl ?purgers ?{
????"127.0.0.1";
"localhost";
“192.168.134.0/24”
????!"192.168.134.1";
}
說明:acl?是訪問列表關鍵字(必需小寫),name?是該列表的別名用于調用,花括號內部是地址集。
注意:如果列表中包含了無法解析的主機地址,它會匹配任何地址。
如果不想讓它匹配可以在前添加一個 ! 符號,如上面 !"192.168.134.1";?
使用ACL只需要用 匹配運算符 ~ 或 !~? 如:
sub ?vcl_recv?{
????if? (req.method?==?"PURGE")?{ ? ?//PURGE請求的處理
??????? ?if ?(client.ip ?~ ?purgers)?{
???????????? ?return(purge);
??????? ?}?else?{
????????????return(synth(403,?"Access?denied."));
????????}
????}
}
?
5)緩存規則配置:
sub vcl_recv {
? // PURGE請求的處理
? if (req.method == "PURGE") {
? ? if (!client.ip ~ purgers) {
? ? ? return (synth(405, "Not Allowed."));
? ? }
? ? return (purge);
? }
?
? set req.backend_hint = web.backend();
?
? //將php、asp等動態內容訪問請求直接發給后端服務器,不緩存。
? if (req.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") {
? ? return (pass);
? }
? //將非GET和HEAD訪問請求直接發給后端服務器,不緩存。例如POST請求。
? if (req.method != "GET" && req.method != "HEAD") {
? ? ? ? return (pass);
? }
? //如果varnish看到header中有'Authorization'頭,它將pass請求。
? if (req.http.Authorization) {
? ? ? ? return (pass);
}
?
? //帶cookie首部的GET請求也緩存
? if (req.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") {
? ? unset req.http.cookie;
? ? return (hash);
? }
說明:默認情況,varnish不緩存從后端響應的http頭中帶有Set-Cookie的對象。如果客戶端發送的請求帶有Cookie header,varnish將忽略緩存,直接將請求傳遞到后端。
?
//為發往后端主機的請求添加X-Forward-For首部,首次訪問增加 X-Forwarded-For 頭信息,方便后端程序獲取客戶端ip,而不是varnish地址
if (req.restarts == 0) {
? ? ? ? if (req.http.x-forwarded-for) {//如果設置過此header則要再次附加上用逗號隔開
? ? ? ? ? ? set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
? ? ? ? } else {//如果只有一層代理的話,就無需設置了
? ? ? ? ? ? set req.http.X-Forwarded-For = client.ip;
? ? ? ? }
}
說明:X-Forwarded-For 是用來識別通過HTTP代理或負載均衡方式連接到Web服務器的客戶端最原始的IP地址的HTTP請求頭字段
子程序:
?
子程序是一種類似C的函數,但是程序沒有調用參數,子程序以 sub 關鍵字定義。在VCL里子程序是用于管理程序。
注意:所有VCL內置的程序都是以 vcl_ 開頭,并已經預置好,在VCL文件中只要聲明對應的內置子程序,都會在對應的流程中調用。
三、varnish完整配置實例
1、拓撲環境
Varnish:192.168.31.250
Web01:192.168.31.83
Web02:192.168.31.141
配置web01、web02做為后端服務器(過程略)
確保varnish服務器能正常訪問web01、web02
Varnish緩存代理服務器配置:
2、vcl文件配置內容:
[root@varnish ~]# cat ?/usr/local/var/varnish/default.vcl
#使用varnish版本4的格式.
vcl 4.0;
#加載后端負載均衡模塊
import directors;
#加載std模塊
import std;
#創建名為backend_healthcheck的健康檢查策略
probe backend_healthcheck {
? ? .url="/";
? ? .interval = 5s;
? ? .timeout = 1s;
? ? .window = 5;
? ? .threshold = 3;
}
#定義后端服務器
backend web_app_01 {
? ? .host = "192.168.31.83";
? ? .port = "80";
? ? .first_byte_timeout = 9s;
? ? .connect_timeout = 3s;
? ? .between_bytes_timeout = 1s;
? ? .probe = backend_healthcheck;
}
backend web_app_02 {
? ? .host = "192.168.31.141";
? ? .port = "80";
? ? .first_byte_timeout = 9s;
? ? .connect_timeout = 3s;
? ? .between_bytes_timeout = 1s;
? ? .probe = backend_healthcheck;
}
#定義允許清理緩存的IP
acl purgers {
? ? "127.0.0.1";
? ? "localhost";
? ? "192.168.31.0/24";
}
#vcl_init初始化子程序創建后端主機組
sub vcl_init {
? ? new web = directors.round_robin();
? ? web.add_backend(web_app_01);
? ? web.add_backend(web_app_02);
}
#請求入口,用于接收和處理請求。這里一般用作路由處理,判斷是否讀取緩存和指定該請求使用哪個后端
sub vcl_recv {
? ? #將請求指定使用web后端集群?.在集群名后加上 .backend()
set req.backend_hint = web.backend();
# 匹配清理緩存的請求
? ? if (req.method == "PURGE") {
? ? ? ? if (!client.ip ~ purgers) {
? ? ? ? ? ? return (synth(405, "Not Allowed."));
? ? ? ? }
? ? ? ? # 是的話就執行清理
? ? ? ? return (purge);
? ? }
? ? # 如果不是正常請求 就直接穿透沒商量
? ? if (req.method != "GET" &&
? ? ? req.method != "HEAD" &&
? ? ? req.method != "PUT" &&
? ? ? req.method != "POST" &&
? ? ? req.method != "TRACE" &&
? ? ? req.method != "OPTIONS" &&
? ? ? req.method != "PATCH" &&
? ? ? req.method != "DELETE") {
? ? ? return (pipe);
? ? }
? ? # 如果不是GET和HEAD就跳到pass
? ? if (req.method != "GET" && req.method != "HEAD") {
? ? ? ? return (pass);
? ? }
? ? #如果匹配動態內容訪問請求就跳到pass
? ? if (req.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") {
? ? ? ? return (pass);
? ? }
? ? #具有身份驗證的請求跳到pass
? ? if (req.http.Authorization) {
? ? ? ? return (pass);
? ? }
?
? ? if (req.http.Accept-Encoding) {
? ? ? ? if (req.url ~ "\.(bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)$") {
? ? ? ? ? ? unset req.http.Accept-Encoding;
? ? ? ? } elseif (req.http.Accept-Encoding ~ "gzip") {
? ? ? ? ? ? set req.http.Accept-Encoding = "gzip";
? ? ? ? } elseif (req.http.Accept-Encoding ~ "deflate") {
? ? ? ? ? ? set req.http.Accept-Encoding = "deflate";
? ? ? ? } else {
? ? ? ? ? ? unset req.http.Accept-Encoding;
? ? ? ? }
? ? }
?
? ? if (req.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") {
? ? ? ? unset req.http.cookie;
? ? ? ? return (hash);
}
# 把真實客戶端IP傳遞給后端服務器 后端服務器日志使用X-Forwarded-For來接收
? ? if (req.restarts == 0) {
? ? ? ? if (req.http.X-Forwarded-For) {
? ? ? ? ? ? set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
? ? ? ? } else {
? ? ? ? ? ? set req.http.X-Forwarded-For = client.ip;
? ? ? ? }
? ? }
? ? return (hash);
}
# hash事件(緩存事件)
sub vcl_hash {
? ? hash_data(req.url);
? ? if (req.http.host) {
? ? ? ? hash_data(req.http.host);
? ? } else {
? ? ? ? hash_data(server.ip);
? ? }
? ? return (lookup);
}
# 緩存命中事件
sub vcl_hit {
? ? if (req.method == "PURGE") {
? ? ? ? return (synth(200, "Purged."));
? ? }
? ? return (deliver);
}
# 緩存不命中事件
sub vcl_miss {
? ? if (req.method == "PURGE") {
? ? ? ? return (synth(404, "Purged."));
? ? }
? ? return (fetch);
}
# 返回給用戶的前一個事件 通常用于添加或刪除header頭
sub vcl_deliver {
? ? if (obj.hits > 0) {
? ? ? ? set resp.http.X-Cache = "HIT";
? ? ? ? set resp.http.X-Cache-Hits = obj.hits;
? ? } else {
? ? ? ? set resp.http.X-Cache = "MISS";
}
#取消顯示php框架版本的header頭
unset resp.http.X-Powered-By;
#取消顯示web軟件版本、Via(來自varnish)等header頭 為了安全
? ? unset resp.http.Server;
? ? unset resp.http.X-Drupal-Cache;
? ? unset resp.http.Via;
? ? unset resp.http.Link;
unset resp.http.X-Varnish;
#顯示請求經歷restarts事件的次數
set resp.http.xx_restarts_count = req.restarts;
#顯示該資源緩存的時間單位秒
set resp.http.xx_Age = resp.http.Age;
#顯示該資源命中的次數
set resp.http.hit_count = obj.hits;
#取消顯示Age 為了不和CDN沖突
unset resp.http.Age;
#返回給用戶
? ? return (deliver);
}
# pass事件
sub vcl_pass {
? ? return (fetch);
}
#處理對后端返回結果的事件(設置緩存、移除cookie信息、設置header頭等) 在fetch事件后自動調用
sub vcl_backend_response {
? ? #開啟grace模式 表示當后端全掛掉后 即使緩存資源已過期(超過緩存時間) 也會把該資源返回給用戶 資源最大有效時間為5分鐘
set beresp.grace = 5m;
#后端返回如下錯誤狀態碼 則不緩存
? ? if (beresp.status == 499 || beresp.status == 404 || beresp.status == 502) {
? ? set beresp.uncacheable = true;
}
#如請求php或jsp 則不緩存
? ? if (bereq.url ~ "\.(php|jsp)(\?|$)") {
? ? ? set beresp.uncacheable = true;
? ? } else { //自定義緩存文件的緩存時長,即TTL值
? ? ? ?if (bereq.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico)($|\?)") {
? ? ? ? ?set beresp.ttl = 15m;
? ? ? ? ?unset beresp.http.Set-Cookie;
? ? ? ?} elseif (bereq.url ~ "\.(gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") {
? ? ? ? ? ?set beresp.ttl = 30m;
? ? ? ? ? ?unset beresp.http.Set-Cookie;
? ? ? ? ?} else {
? ? ? ? ? ? set beresp.ttl = 10m;
? ? ? ? ? ? unset beresp.http.Set-Cookie;
? ? ? ? ?}
? ? ? }
? ? #返回給用戶
? ? return (deliver);
}
sub vcl_purge {
? ? ? ? return (synth(200,"success"));
}
?
sub vcl_backend_error {
? ? if (beresp.status == 500 ||
? ? ? ? beresp.status == 501 ||
? ? ? ? beresp.status == 502 ||
? ? ? ? beresp.status == 503 ||
? ? ? ? beresp.status == 504) {
? ? ? ? return (retry);
? ? }
}
?
sub vcl_fini {
? ? return (ok);
}
?
3、啟動varnish
當啟動varnish時有兩個重要的參數你必須設置: 一個是處理http請求的tcp監聽端口,另一個是處理真實請求的后端server
注:如果你使用操作系統自帶的包管理工具安裝的varnish,你將在下面的文件找到啟動參數:
Red?Hat,?Centos:?/etc/sysconfig/varnish
1):'-a' 參數定義了varnish監聽在哪個地址,并用該地址處理http請求,你可能想設置這個參數在眾所周知的http 80端口.
例子:
-a? :80
-a ?localhost:80
-a? 192.168.1.100:8080
-a? '[fe80::1]:80'
-a ?'0.0.0.0:8080,[::]:8081'
如果你的webserver和varnish運行在同一臺機器,你必須換一個監聽地址.
2):'-f' ?VCL-file ?or ?'-b' ?backend
-f添加vcl文件,-b定義后端server
varnish需要知道從×××到這個需要緩存的http server.你可以用-b參數指定,或者幫把它放在vcl文件中,然后使用-f參數指定.
在啟動的時候使用-b是一個快捷的方式.
-b?192.168.1.2:80
注意:如果你指定的是name,這個name必須能解析成一個IPv4或者IPv6的地址
如果你使用-f參數,你啟動的時候可以在-f指定vcl文件。
默認的varnish使用100M的內存來緩存對象,如果你想緩存更多,可以使用-s參數.?
注:Varnish擁有大量的有用的命令行參數,建議查看其幫助
[root@varnish ~]# /usr/local/sbin/varnishd -h
?
啟動varnish
[root@varnish ~]# /usr/local/sbin/varnishd -f /usr/local/var/varnish/default.vcl -s malloc,200M -a 0.0.0.0:80
[root@varnish ~]# netstat -anpt | grep 80
tcp ? ? 0 ? 0 0.0.0.0:80 ? ? ?0.0.0.0:* ? ? ?LISTEN ? ? ?6173/varnishd
2)現在,varnish已經啟動和運行,你可以通過varnish訪問您的Web應用程序。
打開火狐瀏覽器
第一次訪問
第二次訪問
3)varnish4配置手動清除緩存
varnish4通過vcl配置清楚緩存
通過vcl配置可以讓客戶端手動請求清楚緩存,以保證局部數據及時更新,而不用重啟varnish服務器。
配置方法:
#允許清除緩存IP集
acl purgers {
? ? "127.0.0.1";
? ? "localhost";
? ? "192.168.31.0/24";
}
sub vcl_recv {
? ? ……
? ? if (req.method == "PURGE") {
? ? ? ? if (!client.ip ~ purgers) {
? ? ? ? ? ? return (synth(405, "Not Allowed."));
? ? ? ? }
? ? ? ? return (purge); ? //清除緩存
}
……
}
sub vcl_purge {
? ? ? ? return (synth(200,"success"));
}
打開火狐瀏覽器,隨便進入一個緩存頁面,如下圖所示。
點擊編輯和重發, 修改請求類型為?PURGE?再點擊?發送
查看返回狀態,如果成功則成功清除緩存,可以按 F5 刷新頁面,查看新內容。
轉載于:https://blog.51cto.com/namesam/1908525