Varnish的VCL子程式 以下內容參考: http://book.varnish software.com/4.0/ VCL子進程,在其中定製Varnish的行為。VCL子常式可用於:添加自定義標頭,更改Varnish錯誤消息的外觀,在Varnish中添加HTTP重定向功能,清除內容以及定義緩存 ...
Varnish的VCL子程式
以下內容參考:
http://book.varnish-software.com/4.0/
VCL子進程,在其中定製Varnish的行為。VCL子常式可用於:添加自定義標頭,更改Varnish錯誤消息的外觀,在Varnish中添加HTTP重定向功能,清除內容以及定義緩存對象的哪些部分是唯一的。
註意:強烈建議儘可能讓預設的內置子程式。內置子程式的設計考慮到安全性,這通常意味著它們可以合理的方式處理VCL代碼中的任何缺陷。
vcl_recv
規範化客戶端輸入
選擇一個後端Web伺服器
重新編寫Web應用程式的客戶端數據
根據客戶端輸入決定緩存策略
訪問控制列表(ACL)
安全屏障,例如針對SQL註入攻擊
修複錯誤,例如index.htlm- >index.html
vcl_recv是Varnish第一個VCL子進程,將客戶端請求解析為其基本數據結構之後執行。 vcl_recv有四個主要用途:
修改客戶端數據以減少緩存的多樣性。
決定使用哪個Web伺服器。
根據客戶端數據決定緩存策略。
執行特定Web應用程式所需的重寫規則。
在vcl_recv你可以執行以下終止操作:
pass:它通過緩存查找,但它執行Varnish請求流的其餘部分。 pass不會將來自後端的響應存儲在緩存中。
pipe:此操作創建一個全雙工管道,將客戶端請求轉發到後端,且不查看其內容。後端回覆被轉發回客戶端且不緩存其內容。由於Varnish不再嘗試將內容映射到請求上,因此任何子進程的請求發送給活動連接將被通過pipe轉發。pipe請求不會出現在任何日誌中。
hash:它在緩存中查找請求。
purge:它在緩存中查找請求以便刪除它。
synth -從Varnish生成合成響應。這種合成響應通常是一個帶有錯誤信息的網頁。 synth也可以用來重定向客戶端請求。
同樣可以使用vcl_recv來設置以下安全措施。varnish不是入侵檢測系統的替代品,但仍可以用來提前阻止一些典型的攻擊。簡單訪問控制列表(ACL)也可以應用到vcl_recv上。
內建的vcl_recv子進程不會緩存所有你想要的,同時也最好不要緩存錯誤內容而是把它們發送給錯誤的用戶。
重新訪問內置的vcl_recv:
sub vcl_recv {
if (req.method == "PRI") {
/* We do not support SPDY or HTTP/2.0 */
return (synth(405));
}
if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "DELETE") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
if (req.method != "GET" && req.method != "HEAD") {
/* We only deal with GET and HEAD by default */
return (pass);
}
if (req.http.Authorization || req.http.Cookie) {
/* Not cacheable by default */
return (pass);
}
return (hash);
}
示例:
基本設備檢測
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_pass
進入pass模式是調用
sub vcl_pass {
return (fetch);
}
當上一層子進程返回pass動作後才會調用vcl_pass子進程。這動作的請求是在pass模式中設置的。vcl_pass通常作為一個重要的catch-all,服務於vcl_hit和vcl_miss執行結果。
vcl_pass可能會返回是三個動作:fetch、synth、或者是restart。當返回的的是fetch時,正在進行的請求就採用pass模式。採用pass模式從請求中抓取的對象不被緩存,但會傳遞到客戶端。synth和restart返回的動作會調用相關的子進程。
hit-for-pass
當一個對象不應該被緩存是使用
hit-for-pass對象取代抓取的對象
存在TTL
一些請求就不應該被緩存,一個典型的例子就是當一個請求頁中含有set-cookie響應頭部時,且 必須並只能把它遞送給所需的客戶端。因此你可以告訴varnish創建個hit-for-pass的對象並存儲這個對象到緩存,而不是存儲抓取的這個對象。分散式的請求被採用pass模式處理。
當一個對象不需要被緩存是,beresp.uncacheable變數會設置為true。結果,cacher進程會保持對hit-for-pass對象的hash散列應用。這種情況下,對請求的查找操作會傳遞給hash來找個hit-for-pass對象。如此類的請求會被vclpass子進程中的pass模式給處理。
如同其他緩存對象一樣,hit-for-pass對象也有一個TTL(生命周期)。一旦生命周期過了,這個對象就會從緩存上刪除。
vcl_backend_fetch
sub vcl_backend_fetch {
return (fetch);
}
vcl_backend_fetch 可以從vcl_miss或vcl_pass中調用。當vcl_backend_fetch從vcl_miss中調用時,抓取的對象會被緩存。如果vcl_backend_fetch被從vcl_pass中調用時,抓取的對象也不會被緩存的,即使是obj.ttl或obj.keep變數的值比0大。
一個相關的變數是bereq.uncacheable,這個變數指示出從後端來的對象請求是否被緩存。當然從pass請求中來的對象是絕不被緩存的。
vcl_backend_fetch有倆個可能的終端操作,fetch或abandon。fetch動作發送請求給後動,abandon動作調用vcl_synth子進程。內建vcl_bakend_fetch子進程只返回fetch動作。
後端響應被vcl_backend-response還是vcl_backend_error處理取決於響應來之於那個服務。如果Varnish收到語法正確的HTTP響應,則Varnish將控制權交給vcl_backend_response。語法正確的HTTP響應包括HTTP 5xx錯誤代碼。如果Varnish沒有收到HTTP響應,則將控制權交給vcl_backend_error。
vcl_hash
定義什麼是唯一的請求
vcl_hash終是在vcl_recv後,或者另個子進程範圍hash動作關鍵詞。
sub vcl_hash {
hash_data(req.url);
if (req.http.host) {
hash_data(req.http.host);
} else {
hash_data(server.ip);
}
return (lookup);
}
vcl_hash定義要用於緩存對象的hash key。Hash key將一個緩存對象與另一個緩存對象區分開來。預設的VCL為vcl_hash添加主機名或ip地址,同時添加請求的url給cache hash。
vcl_hash的一個用法是在cache hash上添加用戶名來識別用戶指定的數據。當然緩存用戶數據時應該謹慎進行。一個更好的選擇可能是hash每個會話緩存對象。
vcl_hash子進程返回lookup操作關鍵字。不像其他動作關鍵詞,lookup是一個操作,而不是子進程。在vcl_hash後的下個狀態取決於在緩存中lookup的查找。
當lookup操作沒能匹配到任何hash時,它會創建一個帶有busy標誌的對象並存儲在緩存中。然後,請求會被髮送到vcl_miss子進程中。一旦請求被處理busy標誌會被刪除,並從後端的響應中更新對象。
隨後遇到busy標記的對象請求將被髮送到等待列表中。這個等待名單旨在提高響應性能,這個在waiting state 選項中有解釋。
註意:一個高速緩存散列可以指代一個或多個對象變數。對象變數是基於Vary頭域的創建的。在一個緩存散列下保留多個變數是比較好的做法,而不是每個變數創建一個散列。
vcl_hit
在lookup操作之後執行,調用vcl_hash,找到(hits)在緩存上的對象。
sub vcl_hit {
if (obj.ttl >= 0s) {
// A pure unadultered hit, deliver it
return (deliver);
}
if (obj.ttl + obj.grace > 0s) {
// Object is in grace, deliver it
// Automatically triggers a background fetch
return (deliver);
}
// fetch & deliver once we get the result
return (fetch);
}
vcl_hit子進程通常通過調用含有deliver,restart或者synth的return()來進行終止。
如果對象的TTL+grace time沒有過時的話,返回的deliver會控制vcl_deliver。如果過時時間超過了TTL,但沒有超過TTL+grace time,deliver會調用與vcl_deliver同步的background fetch。background fetch是一種非同步調用,用來插入一個新的請求對象到緩存中。grace time會在grace模式選項中有解釋。
restart重啟傳輸,並增加重啟計數器設定值。如果重啟的次數比max_restarts設定的值要大,varnish會發出一個guru mediation的錯誤。
synth(status code,reason)返回指定狀態碼給客戶端並丟棄請求。
vcl_miss
如果一個請求對象沒有被lookup操作找到時子進程會被調用。
包含有是否嘗試從後端檢索文檔以及使用那個後端的策略。
sub vcl_miss {
return (fetch);
}
子進程vcl_hit和vcl_miss是相關的。你很少調用他們,因為HTTP請求投吧的修改通常是在vcl_recv中進行。但是,如果你不希望發送X-Varnish頭部給後端服務,你可以把它移動動vcl_miss或vcl_pass中。基於這種情況,你可以使用unset bereq ,http,x-varnish。
vcl_deliver
所有請求流程的公共最後退出點,除了通過vcl_pipe的請求。
經常用於添加和移除debug-headers。
sub vcl_deliver {
return (deliver);
}
vcl_deliver子進程是簡單的,同樣也是對修改varnish的輸出很有用的。如果你需要刪除一個頭部,或添加一個不應該存儲在cache中的頭部,vcl_deliver可以勝任這個工作。
在vcl_deliver中常用的且被可被修改的變數是:
resp.http.*:發送個客戶端的頭部,它們可以被set和unset。
resp.status:狀態碼為200,404,503等
resp.reason:被返回給客戶端的http狀態信息
obj.hit:在對象上的cache-hits的數。因此,0代表miss,可以評估這個變數來輕鬆地顯示響應是來自緩存命中還是未命中。
req.restarts:在VCL中發出的重啟次數 - 如果沒有發生,則返回0。
vcl_synth
用於在Varnish中生成內容
錯誤消息可以在這裡創建
其他用例:重定向用戶(301/302重定向)
vcl/default-vcl_synth.vcl:
sub vcl_synth {
set resp.http.Content-Type = "text/html; charset=utf-8";
set resp.http.Retry-After = "5";
synthetic( {"<!DOCTYPE html>
<html>
<head>
<title>"} + resp.status + " " + resp.reason + {"</title>
</head>
<body>
<h1>Error "} + resp.status + " " + resp.reason + {"</h1>
<p>"} + resp.reason + {"</p>
<h3>Guru Meditation:</h3>
<p>XID: "} + req.xid + {"</p>
<hr>
<p>Varnish cache server</p>
</body>
</html>
"} );
return (deliver);
}
你可以創建合成響應,例如,在vcl_synth上的個性化錯誤信息。調用這個子進程你可以做:
return (synth(status_code, "reason"));
註意synth不是一個關鍵字,而是個帶有參數的函數。
你必須為vcl_synth明確地返回status code和reason參數。在resp.http上設置合成響應的頭部。
註意:從 vcl/default-vcl_synth.vcl註意到 {" and "}可以用於創建多行的欄位。這個不僅限於synthetic()函數,在其他地址也可以使用。vcl_synth定義的對象絕不在緩存上存儲,對立與vcl_backend_error定義的對象。
示例:
使用vcl_synth重定向請求
sub vcl_recv {
if (req.http.host == "www.example.com") {
set req.http.location = "http://example.com" + req.url;
return (synth(750, "Permanently moved"));
}
}
sub vcl_synth {
if (resp.status == 750) {
set resp.http.location = req.http.location; set resp.status = 301;
return (deliver);
}
}