天天看點

varnish4.0緩存代理超詳細配置以及解析

部落客QQ:819594300

有什麼疑問的朋友可以聯系部落客,部落客會幫你們解答,謝謝支援!

一、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不能從緩存中獲得資料來響應用戶端,它将轉發請求到後端(backendservers),擷取響應同時存儲,最後傳遞給用戶端。

如果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):

<a href="https://s5.51cto.com/wyfs02/M01/95/67/wKioL1kVBqmzycWgAAGvVJ3UeZw569.jpg" target="_blank"></a>

req:Therequest object,請求到達時可用的變量(用戶端發送的請求對象)

bereq:Thebackend request object,向後端主機請求時可用的變量

beresp:Thebackend response object,從後端主機擷取内容時可用的變量(後端響應請求對象)

resp:TheHTTP 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.&lt;name&gt;.free_space:存儲可用空間(位元組數)。

storage.&lt;name&gt;.used_space:存儲已經使用空間(位元組數)。

storage.&lt;name&gt;.happy:存儲健康狀态。

8、特定功能性語句

ban(expression):清除指定對象緩存

call(subroutine):調用子程式,如:call(name);

hash_data(input):生成hash鍵,用于制定hash鍵值生成結構,隻能在vcl_hash子程式中使用。調用 hash_data(input) 後,即這個hash為目前頁面的緩存hash鍵值,無需其它擷取或操作,如:

subvcl_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 子程式中使用,如:

subvcl_synth {

    //自定義内容

    synthetic ({"

&lt;!DOCTYPEHTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&gt;

&lt;htmllang="zh-cn"&gt;

   &lt;head&gt;

   &lt;meta http-equiv="Content-Type"content="text/html; charset=utf-8"/&gt;

      &lt;title&gt;error&lt;/title&gt;

   &lt;/head&gt;

   &lt;body&gt;

      &lt;h1&gt;Error&lt;/h1&gt;

      &lt;h3&gt;這隻是一個測試自定義響應異常内容&lt;/h3&gt;

   &lt;/body&gt;

&lt;/html&gt;

    "});

    //隻傳遞自定義内容

    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(statuscode,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(statuscode,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

pipe  繼續pipe模式,進入後端vcl_backend_fetch子程式,預設傳回值。

vcl_pass子程式:

pass模式處理,該模式類似hash緩存模式,僅不做緩存處理。

fetch  繼續pass模式,進入後端vcl_backend_fetch子程式,預設傳回值。

vcl_hit子程式:

hash緩存模式時,存在hash緩存時調用,用于緩存處理,可放棄或修改緩存。

讀:client,server,obj,req,storage

restart重新開機請求。

deliver傳遞緩存内容,進入vcl_deliver子程式處理,預設傳回值。

vcl_miss子程式:

hash緩存模式時,不存在hash緩存時調用,用于判斷性的選擇進入後端取響應内容,可以修改為pass模式。

pass切換到pass模式,進入vcl_pass子程式。

fetch  正常取後端内容再緩存,進入vcl_backend_fetch子程式,預設傳回值。

vcl_hash子程式:

hash緩存模式,生成hash值作為緩存查找鍵名提取緩存内容,主要用于緩存hash鍵值處理,可使用hash_data(string) 指定鍵值組成結構,可在同一個頁面通過IP或cookie生成不同的緩存鍵值。

lookup查找緩存對象,存在緩存進入vcl_hit子程式,不存在緩存進入vcl_miss子程式,當使用了purge清理模式時會進入vcl_purge子程式,預設傳回值。

vcl_purge子程式:

清理模式,當查找到對應的緩存時清除并調用,用于請求方法清除緩存,并報告。

vcl_deliver子程式:

用戶端傳遞子程式,在vcl_backend_response子程式後調用(非pipe模式),或vcl_hit子程式後調用,可用于追加響應頭資訊,cookie等内容。

讀:client,server,req,resp,obj,storage

寫:client,req,resp

deliver正常傳遞後端或緩存響應内容,預設傳回值。

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子程式,預設傳回值。

retry重試後端請求,重試計數器加1,當超過配置中max_retries值時會報錯并進入vcl_backend_error子程式。

vcl_backend_error子程式:

後端處理失敗調用,異常頁面展示效果處理,可自定義錯誤響應内容,或修改beresp.status與beresp.http.Location重定向等。

deliver隻傳遞 sysnthetic(string) 自定義内容,預設傳回後端異常标準錯誤内容。

vcl_synth子程式:

自定義響應内容。可以通過synthetic()和傳回值 synth 調用,這裡可以自定義異常顯示内容,也可以修改resp.status與resp.http.Location重定向。

讀:client,server,req,resp,storage

寫:req,resp

deliver隻傳遞 sysnthetic(string) 自定義内容,預設傳回 sysnth 異常指定狀态碼與錯誤内容。

vcl_init子程式:

加載vcl時最先調用,用于初始化VMODs,該子程式不參與請求處理,僅在vcl加載時調用一次。

讀:server

寫:無

ok正常傳回,進入vcl_recv子程式,預設傳回值。

vcl_fini子程式:

解除安裝目前vcl配置時調用,用于清理VMODs,該子程式不參與請求處理,僅在vcl正常丢棄後調用。

ok正常傳回,本次vcl将釋放,預設傳回值。

varnish子程式調用流程圖,通過大部分子程式的return傳回值進入下一步行動:

<a href="https://s3.51cto.com/wyfs02/M02/95/67/wKiom1kVBqujVhT6AATfL-bE37g690.jpg" target="_blank"></a>

11)優雅模式(Garcemode)

Varnish中的請求合并

當幾個用戶端請求同一個頁面的時候,varnish隻發送一個請求到後端伺服器,然後讓其他幾個請求挂起并等待傳回結果;獲得結果後,其它請求再複制後端的結果發送給用戶端;

但如果同時有數以千計的請求,那麼這個等待隊列将變得龐大,這将導緻2類潛在問題:

驚群問題(thundering herd problem),即突然釋放大量的線程去複制後端傳回的結果,将導緻負載急速上升;沒有使用者喜歡等待;

故為了解決這類問題,可以配置varnish在緩存對象因逾時失效後再保留一段時間,以給那些等待的請求傳回過去的檔案内容(stalecontent),配置案例如下:

subvcl_recv {

if(! req.backend.healthy) {

setreq.grace = 5m;

}else {

setreq.grace = 15s;

subvcl_fetch {

setberesp.grace = 30m;

以上配置表示varnish将會将失效的緩存對象再多保留30分鐘,此值等于最大的req.grace值即可;

而根據後端主機的健康狀況,varnish可向前端請求分别提供5分鐘内或15秒内的過期内容。

二、安裝varnish

虛拟機環境如下:

<a href="https://s4.51cto.com/wyfs02/M02/95/67/wKioL1kVBqzR8pTMAABn3FoIZCc216.jpg" target="_blank"></a>

1、安裝依賴關系的軟體包(注:使用centos線上yum源)

<a href="https://s4.51cto.com/wyfs02/M02/95/67/wKioL1kVBqzy7ph5AAC4gw52C8k596.jpg" target="_blank"></a>

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/

解壓,進入解壓目錄編譯安裝:

<a href="https://s4.51cto.com/wyfs02/M00/95/67/wKiom1kVBqyTB-5uAADMmc9Xvok144.jpg" target="_blank"></a>

注:

./autogen.sh

如果從Git庫下載下傳的安裝包時才需要運作,用于生成configure編譯檔案。

配置、編譯與安裝:

<a href="https://s1.51cto.com/wyfs02/M01/95/67/wKiom1kVBq2QF9YSAAB2LMUzMPI987.jpg" target="_blank"></a>

注:不指定安裝路徑,預設是安裝在/usr/local目錄下。

上傳我已編号的default.vcl檔案(下文有配置内容)

<a href="https://s1.51cto.com/wyfs02/M00/95/67/wKioL1kVBq3QxNi0AAI4uBJVOFU538.jpg" target="_blank"></a>

預設vatnish沒有配置檔案,我們需要手動配置:

<a href="https://s1.51cto.com/wyfs02/M01/95/67/wKioL1kVBq7BGnO7AADM0FNHLqw742.jpg" target="_blank"></a>

現在我們手動添加配置檔案:

<a href="https://s2.51cto.com/wyfs02/M02/95/67/wKiom1kVBq7inIcHAAENiuTXS18324.jpg" target="_blank"></a>

将剛才上傳的已編号的default.vcl檔案導入到這裡:

<a href="https://s2.51cto.com/wyfs02/M01/95/67/wKiom1kVBq7xIKfFAACc0MSv-R0467.jpg" target="_blank"></a>

具體default.vcl檔案的内容如下:

vcl4.0;

importdirectors;

importstd;

probebackend_healthcheck {

    .url="/"; 

    .interval = 5s;

    .timeout = 1s;

    .window = 5;

    .threshold = 3;

backendweb_app_01 {

    .host = "192.168.1.11";

    .port = "80";

    .first_byte_timeout = 9s;

    .connect_timeout = 3s;

    .between_bytes_timeout = 1s;

    .probe = backend_healthcheck;

backendweb_app_02 {

    .host = "192.168.1.12";

aclpurgers {

    "127.0.0.1";

    "localhost";

    "192.168.1.0/24";

subvcl_init {

    new web = directors.round_robin();

    web.add_backend(web_app_01);

    web.add_backend(web_app_02);

subvcl_recv {   

    set req.backend_hint = web.backend();

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

        if (!client.ip ~ purgers) {

            return (synth(405, "NotAllowed."));

        }

        return (purge);

    }

    if (req.method != "GET"&amp;&amp;

      req.method != "HEAD" &amp;&amp;

      req.method != "PUT" &amp;&amp;

      req.method != "POST" &amp;&amp;

      req.method != "TRACE"&amp;&amp;

      req.method != "OPTIONS"&amp;&amp;

      req.method != "PATCH"&amp;&amp;

      req.method != "DELETE") {

      return (pipe);

    if (req.method != "GET"&amp;&amp; req.method != "HEAD") {

        return (pass);

    if (req.url ~"\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") {

    if (req.http.Authorization) {

    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)$"){

            unsetreq.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);

    if (req.restarts == 0) {

        if (req.http.X-Forwarded-For) {

            set req.http.X-Forwarded-For =req.http.X-Forwarded-For + ", " + client.ip;

            set req.http.X-Forwarded-For =client.ip;

    return (hash);

subvcl_hash {

    hash_data(req.url);

    if (req.http.host) {

        hash_data(req.http.host);

    } else {

        hash_data(server.ip);

    return (lookup);

subvcl_hit {

        return (synth(200,"Purged."));

    return (deliver);

subvcl_miss {

        return (synth(404,"Purged."));

    return (fetch);

subvcl_deliver {

    if (obj.hits &gt; 0) {

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

        set resp.http.X-Cache-Hits = obj.hits;

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

    unset resp.http.X-Powered-By;

    unset resp.http.Server;

    unset resp.http.X-Drupal-Cache;

    unset resp.http.Via;

    unset resp.http.Link;

    unset resp.http.X-Varnish;

    set resp.http.xx_restarts_count =req.restarts;

    set resp.http.xx_Age = resp.http.Age;

    set resp.http.hit_count = obj.hits;

    unset resp.http.Age;

subvcl_pass {

subvcl_backend_response {

    set beresp.grace = 5m;

    if (beresp.status == 499 || beresp.status== 404 || beresp.status == 502) {

    set beresp.uncacheable = true;

    if (bereq.url ~"\.(php|jsp)(\?|$)") {

      set beresp.uncacheable = true;

       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;

         }

      }

subvcl_purge {

        return(synth(200,"success"));

subvcl_backend_error {

    if (beresp.status == 500 ||

        beresp.status == 501 ||

        beresp.status == 502 ||

        beresp.status == 503 ||

        beresp.status == 504) {

        return (retry);

subvcl_fini {

    return (ok);

三、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連接配接對應的伺服器,如果沒有定義或定義錯誤則使用者無法通路正常頁面。

文法格式:

backendname{

    .attribute = "value";

說明:backend 是定義後端關鍵字,name 是目前後端節點的别名,多個後端節點時,name 名不能重複,否則覆寫。花括号裡面定義目前節點相關的屬性(鍵=值)。除預設節點外其它節點定義後必需有調用,否則varnish無法啟動。後端是否正常可以通過std.healthy(backend)判斷。

支援運算符:

=   (指派運算)

==  (相等比較)

~    (比對,可以使用正規表達式,或通路控制清單)

!~    (不比對,可以使用正規表達式,或通路控制清單)

!  (非)

&amp;&amp;   (邏輯與)

||   (邏輯或)

屬性清單:

.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;    //設定最大并發連接配接數,超過這個數後連接配接就會失敗

例:(下面兩個例子結果是一樣的,但第二個例子中更适用于叢集,可以友善批量修改)

backendweb{

    .host="192.168.1.11";

    .port="80";

    .probe={          //直接追加監控塊.probe是一個的參數

        .url="/";

        .timeout=2s;

probeweb_probe{   //監控必需定義在前面,否則後端調用找不到監控塊。

    .url="/";

    .timeout=2s;

    .host="192.168.1.12";

    .probe=web_probe;   //調用外部共用監控塊

2)螢幕定義:

指令:probe 。監控可以循環通路指定的位址,通過響應時間判定伺服器是否空閑或正常。這類指令非常适用于叢集中某些節點伺服器崩潰或負載過重,而禁止通路這台節點伺服器。

probename{

說明: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

        .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。

importdirectors;                # load thedirectors

backendweb1 {

.host= "192.168.1.11";

.port= "80";

.probe= backend_healthcheck;

backendweb2 {

.host= "192.168.1.12";

//初始化處理

subvcl_init {            //調用vcl_init初始化子程式建立後端主機組,即directors

    new web_cluster = directors.round_robin(); //使用new關鍵字建立drector對象,使用round_robin算法

    web_cluster.add_backend(web1);   //添加後端伺服器節點

web_cluster.add_backend(web2);

//開始處理請求

subvcl_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

backendimg1 {

     .host = "img1.lnmmp.com";

     .port = "80";

     .probe = backend_healthcheck;

backendimg2 {

     .host = "img2.lnmmp.com";

newimg_cluster = directors.random();

img_cluster.add_backend(img1,2);   //添加後端伺服器節點,并且設定權重值

img_cluster.add_backend(img2,5);

//根據不同的通路域名,分發至不同的後端主機組

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)^images.benet.com$") {

        set  req.backend_hint =img_cluster.backend();

     }

說明:中的i就是忽略大小寫的意思。(?i)表示開啟忽略大小寫,而(?-i)表示關閉忽略大小寫

4)通路控制清單(ACL)

建立一個位址清單,用于後面的判斷,可以是域名或IP集合。這個可以用于指定某些位址請求入口,防止惡意請求等。

acl  purgers {

"localhost";

“192.168.1.0/24”

    !"192.168.1.100";

說明:acl 是通路清單關鍵字(必需小寫),name 是該清單的别名用于調用,花括号内部是位址集。

注意:如果清單中包含了無法解析的主機位址,它會比對任何位址。

如果不想讓它比對可以在前添加一個 ! 符号,如上面 !"192.168.1.100";

使用ACL隻需要用 比對運算符 ~ 或!~  如:

sub  vcl_recv {

    if (req.method == "PURGE") {   //PURGE請求的處理

         if (client.ip  ~  purgers) {    

              return(purge);

            return(synth(403, "Accessdenied."));

5)緩存規則配置:

  // PURGE請求的處理

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

    if (!client.ip ~ purgers) {

      return (synth(405, "NotAllowed."));

    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" &amp;&amp;req.method != "HEAD") {

  //如果varnish看到header中有'Authorization'頭,它将pass請求。

  if (req.http.Authorization) {

  //帶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;

說明:預設情況,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則要再次附加上用逗号隔開

        } else {//如果隻有一層代理的話,就無需設定了

說明:X-Forwarded-For 是用來識别通過HTTP代理或負載均衡方式連接配接到Web伺服器的用戶端最原始的IP位址的HTTP請求頭字段

子程式:

子程式是一種類似C的函數,但是程式沒有調用參數,子程式以 sub 關鍵字定義。在VCL裡子程式是用于管理程式。

注意:所有VCL内置的程式都是以 vcl_ 開頭,并已經預置好,在VCL檔案中隻要聲明對應的内置子程式,都會在對應的流程中調用。

三、varnish完整配置執行個體

1、拓撲環境

Varnish:192.168.1.9

Web01:192.168.1.11

Web02:192.168.1.12

配置web01、web02做為後端伺服器

<a href="https://s3.51cto.com/wyfs02/M01/95/67/wKioL1kVBq_i70z8AADd0-sHzP0007.jpg" target="_blank"></a>

<a href="https://s3.51cto.com/wyfs02/M02/95/67/wKiom1kVBq-yRZv2AABDN07VsmQ813.jpg" target="_blank"></a>

<a href="https://s3.51cto.com/wyfs02/M02/95/67/wKioL1kVBrDCbdFAAADV7O1lHyw467.jpg" target="_blank"></a>

<a href="https://s3.51cto.com/wyfs02/M02/95/67/wKioL1kVBrChQcWrAACMiveSqik011.jpg" target="_blank"></a>

<a href="https://s3.51cto.com/wyfs02/M00/95/67/wKiom1kVBrCRKQ35AAERl4kAvSA018.jpg" target="_blank"></a>

同理,apache2也按此操作。為了驗證方面,網頁内容不要一樣。

確定varnish伺服器能正常通路web01、web02

Varnish緩存代理伺服器配置:

2、vcl檔案配置内容:

[root@varnish~]# cat /usr/local/var/varnish/default.vcl

#使用varnish版本4的格式.

#加載後端負載均衡子產品

#加載std子產品

#建立名為backend_healthcheck的健康檢查政策

#定義後端伺服器

    .host = "192.1681.11";

#定義允許清理緩存的IP

#vcl_init初始化子程式建立後端主機組

#請求入口,用于接收和處理請求。這裡一般用作路由處理,判斷是否讀取緩存和指定該請求使用哪個後端

    #将請求指定使用web後端叢集 .在叢集名後加上 .backend()

setreq.backend_hint = web.backend();

#比對清理緩存的請求

        # 是的話就執行清理

    # 如果不是正常請求 就直接穿透沒商量

      req.method != "DELETE") {

    # 如果不是GET和HEAD就跳到pass

    #如果比對動态内容通路請求就跳到pass

    #具有身份驗證的請求跳到pass

            unset req.http.Accept-Encoding;       

#把真實用戶端IP傳遞給後端伺服器 後端伺服器日志使用X-Forwarded-For來接收

#hash事件(緩存事件)

#緩存命中事件

#緩存不命中事件

#傳回給使用者的前一個事件通常用于添加或删除header頭

#取消顯示php架構版本的header頭

unsetresp.http.X-Powered-By;

#取消顯示web軟體版本、Via(來自varnish)等header頭為了安全

unsetresp.http.X-Varnish;

#顯示請求經曆restarts事件的次數

setresp.http.xx_restarts_count = req.restarts;

#顯示該資源緩存的時間機關秒

setresp.http.xx_Age = resp.http.Age;

#顯示該資源命中的次數

setresp.http.hit_count = obj.hits;

#取消顯示Age為了不和CDN沖突

unsetresp.http.Age;

#傳回給使用者

#pass事件

#處理對後端傳回結果的事件(設定緩存、移除cookie資訊、設定header頭等) 在fetch事件後自動調用

    #開啟grace模式 表示當後端全挂掉後 即使緩存資源已過期(超過緩存時間) 也會把該資源傳回給使用者 資源最大有效時間為5分鐘

setberesp.grace = 5m;

#後端傳回如下錯誤狀态碼 則不緩存

#如請求php或jsp 則不緩存

    } else { //自定義緩存檔案的緩存時長,即TTL值

       if (bereq.url ~"\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico)($|\?)") {

         unset beresp.http.Set-Cookie;

           unset beresp.http.Set-Cookie;

            unset beresp.http.Set-Cookie;

    #傳回給使用者

3、啟動varnish

當啟動varnish時有兩個重要的參數你必須設定: 一個是處理http請求的tcp監聽端口,另一個是處理真實請求的後端server

注:如果你使用作業系統自帶的包管理工具安裝的varnish,你将在下面的檔案找到啟動參數:

RedHat, 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是一個快捷的方式.

-b192.168.1.2:80

注意:如果你指定的是name,這個name必須能解析成一個IPv4或者IPv6的位址

如果你使用-f參數,你啟動的時候可以在-f指定vcl檔案。

預設的varnish使用100M的記憶體來緩存對象,如果你想緩存更多,可以使用-s參數.

注:Varnish擁有大量的有用的指令行參數,建議檢視其幫助

[root@varnish~]# /usr/local/sbin/varnishd -h

啟動varnish

<a href="https://s5.51cto.com/wyfs02/M00/95/67/wKiom1kVBrHi9UX0AAC8Kfox9pQ700.jpg" target="_blank"></a>

<a href="https://s5.51cto.com/wyfs02/M00/95/67/wKioL1kVBrHC2oAXAACfbfsrm3k136.jpg" target="_blank"></a>

防火牆開啟80端口例外:

<a href="https://s5.51cto.com/wyfs02/M01/95/67/wKiom1kVBrGBwJ2XAAEs-T-_oPE630.jpg" target="_blank"></a>

<a href="https://s5.51cto.com/wyfs02/M01/95/67/wKioL1kVBrLR0vk9AAFo399T6Z4619.jpg" target="_blank"></a>

2)現在,varnish已經啟動和運作,你可以通過varnish通路您的Web應用程式。

打開火狐浏覽器

第一次通路:

<a href="https://s4.51cto.com/wyfs02/M02/95/67/wKiom1kVBrPA86pBAAHygT_Z7WI907.jpg" target="_blank"></a>

第二次通路:

<a href="https://s3.51cto.com/wyfs02/M00/95/67/wKioL1kVBrTiU-_oAAJEfpob8VU597.jpg" target="_blank"></a>

第三次通路:

<a href="https://s3.51cto.com/wyfs02/M02/95/67/wKioL1kVBrWgHRbKAAJE-VnNZT8015.jpg" target="_blank"></a>

3)varnish4配置手動清除緩存

varnish4通過vcl配置清楚緩存

通過vcl配置可以讓用戶端手動請求清楚緩存,以保證局部資料及時更新,而不用重新開機varnish伺服器。

配置方法:

#允許清除緩存IP集

    ……

        return (purge);   //清除緩存

……

打開火狐浏覽器,随便進入一個緩存頁面,如下圖所示。

就還用上面的那個截圖吧:

<a href="https://s3.51cto.com/wyfs02/M00/95/68/wKiom1kVCaXRxWQuAAJE-VnNZT8130.jpg" target="_blank"></a>

點選編輯和重發, 修改請求類型為 PURGE 再點選發送:

<a href="https://s3.51cto.com/wyfs02/M01/95/67/wKiom1kVBrXQeHYRAACzC7qX8Q8537.jpg" target="_blank"></a>

<a href="https://s3.51cto.com/wyfs02/M00/95/67/wKiom1kVBrXzQNG7AADD4if5YXk497.jpg" target="_blank"></a>

<a href="https://s2.51cto.com/wyfs02/M01/95/67/wKioL1kVBrajqXb_AADCQMuQF4o324.jpg" target="_blank"></a>

檢視傳回狀态,如果成功則成功清除緩存,可以按 F5 重新整理頁面,檢視新内容。

4)驗證健康檢查

我們模拟後端的apache1當機:

<a href="https://s2.51cto.com/wyfs02/M00/95/67/wKioL1kVBraAzdQMAACd8pJRNKE706.jpg" target="_blank"></a>

然後再浏覽192.168.1.9,檢視:

無論你怎麼重新整理頁面,一直都是apache2 web伺服器提供的網頁,不會出現卡頓現象,證明成功了!

<a href="https://s1.51cto.com/wyfs02/M01/95/67/wKiom1kVBrfy-jNiAADl50z8J8M520.jpg" target="_blank"></a>

現在把apache1的httpd服務再開啟來:

<a href="https://s1.51cto.com/wyfs02/M01/95/67/wKioL1kVBreBlQl6AACkXGQRPBk971.jpg" target="_blank"></a>

再次檢視:

<a href="https://s1.51cto.com/wyfs02/M02/95/67/wKiom1kVBrfwzNwbAAFpNORJ5hU442.jpg" target="_blank"></a>

<a href="https://s2.51cto.com/wyfs02/M02/95/67/wKiom1kVBrix5FM1AADevHXbvCE549.jpg" target="_blank"></a>

從上圖看到apache1的網頁又浏覽到了,再次說明健康檢查驗證成功!

5)去後端web伺服器檢視httpd日志,看一次記錄的用戶端的位址是varnish代理伺服器的還是真實的客戶機IP。

<a href="https://s2.51cto.com/wyfs02/M00/95/67/wKiom1kVBrnCDUAUAALc3rvrePE977.jpg" target="_blank"></a>

從上圖可以看到,記錄的ip是varnish代理伺服器的ip,那我想從日志裡檢視到真實的用戶端ip怎麼辦?

解決辦法如下:

<a href="https://s2.51cto.com/wyfs02/M00/95/67/wKioL1kVBrmCeqN0AADYlzyCofw152.jpg" target="_blank"></a>

<a href="https://s2.51cto.com/wyfs02/M01/95/67/wKioL1kVBtWw3BfUAAEERs2VM5w899.jpg" target="_blank"></a>

<a href="https://s1.51cto.com/wyfs02/M02/95/67/wKioL1kVBtWSsc0lAACwuX-gfrk478.jpg" target="_blank"></a>

同理,apache2也做同樣的操作。

再次從用戶端通路varnish,然後再次檢視通路日志:

<a href="https://s1.51cto.com/wyfs02/M00/95/67/wKiom1kVBtXyQmeNAACVMBYBIVw633.jpg" target="_blank"></a>

注:192.168.1.5是我用戶端的ip位址。

<a href="https://s2.51cto.com/wyfs02/M02/95/67/wKiom1kVBtbACde1AADiTb5g4LU921.jpg" target="_blank"></a>

從上上圖可以看到,通路日志中記錄的不再是varnish的ip位址,而是真實的用戶端ip位址。

本文轉自Mr大表哥 部落格,原文連結: http://blog.51cto.com/zpf666/1924816    如需轉載請自行聯系原作者