Varnish 是一款高性能且開源的反向代理服務器和 HTTP 加速器,其採用全新的軟件體系機構,和如今的硬件體系緊密配合,與傳統的squid 相比,varnish 具備性能更高、速度更快、管理更加方便等諸多優勢,不少大型的網站都開始嘗試使用 varnish 來替換 squid,這些都促進varnish 迅速發展起來。html
varnish主要運行兩個進程:Management進程和Child進程(也叫Cache進程)。它們的工做原理大體以下圖:web
Management進程主要實現應用新的配置、編譯VCL、監控varnish、初始化varnish以及提供一個命令行接口等。Management進程會每隔幾秒鐘探測一下Child進程以判斷其是否正常運行,若是在指定的時長內未獲得Child進程的迴應,Management將會重啓此Child進程。 正則表達式
Child進程包含多種類型的線程,常見的如:Acceptor線程:接收新的鏈接請求並響應;Worker線程:child進程會爲每一個會話啓動一個worker線程,所以,在高併發的場景中可能會出現數百個worker線程甚至更多;Expiry線程:從緩存中清理過時內容; 編程
Varnish依賴「工做區(workspace)」以下降線程在申請或修改內存時出現競爭的可能性。在varnish內部有多種不一樣的工做區,其中最關鍵的當屬用於管理會話數據的session工做區。後端
爲了與系統的其它部分進行交互,Child進程使用了能夠經過文件系統接口進行訪問的共享內存日誌(shared memory log),所以,若是某線程須要記錄信息,其僅須要持有一個鎖,然後向共享內存中的某內存區域寫入數據,再釋放持有的鎖便可。而爲了減小競爭,每一個worker線程都使用了日誌數據緩存。 瀏覽器
共享內存日誌大小通常爲90M,其分爲兩部分,前一部分爲計數器,後半部分爲客戶端請求的數據。varnish提供了多個不一樣的工具如varnishlog、varnishncsa或varnishstat等來分析共享內存日誌中的信息並可以以指定的方式進行顯示。緩存
varnish支持多種不一樣類型的後端存儲,這能夠在varnishd啓動時使用-s選項指定。後端存儲的類型包括:bash
(1)file: 使用特定的文件存儲所有的緩存數據,並經過操做系統的mmap()系統調用將整個緩存文件映射至內存區域(若是條件容許);服務器
(2)malloc: 使用malloc()庫調用在varnish啓動時向操做系統申請指定大小的內存空間以存儲緩存對象,相似於C語言中的malloc動態申請函數;session
file和malloc存儲方法相似,重啓緩存服務時,先前緩存的數據不復存在。
說道varnish的狀態引擎,不得不說vcl(Varnish Configuration Language:varnish配置緩存策略的工具)。它是基於域的一種簡單的編程語言,支持算數運算、容許使用正則表達式、支持if語句等。使用vcl語言編寫的緩存策略一般保存於.vcl文件中,其須要編譯成二進制的格式後才能由varnish調用。
VCL用於讓管理員定義緩存策略,而定義好的策略將由varnish的management進程分析、轉換成C代碼、編譯成二進制程序並鏈接至child進程。varnish內部有幾個所謂的狀態(state),在這些狀態上能夠附加經過VCL定義的策略以完成相應的緩存處理機制,所以VCL也常常被稱做「域專用」語言或狀態引擎,「域專用」指的是有些數據僅出現於特定的狀態中。
具體的狀態的是經過定義內置函數來實現的,具體過程以下圖:
vcl各狀態引擎的功用:
vcl_recv:是在Varnish完成對請求報文的解碼爲基本數據結構後第一個要執行的子例程
vcl_fetch:根據服務器端的響應做出緩存決策
vcl_pipe:用於將請求直接發日後端主機;
vcl_hash:自定義hash生成時的數據來源
vcl_pass:用於將請求直接傳遞至後端主機;
vcl_hit:從緩存中查找到緩存對象時要執行的操做;
vcl_miss:從緩存中款查找到緩存對象時要執行的操做;
vcl_deliver:將用戶請求的內容響應給客戶端時用到的方法;
vcl_error:在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」的結果;
# rpm包下載地址:https://repo.varnish-cache.org/
rpm -ivh varnish-3.0.5-1.el6.x86_64.rpm varnish-libs-3.0.5-1.el6.x86_64.rpm \
varnish-docs-3.0.5-1.el6.x86_64.rpm
修改varnish的相關參數:
## /etc/sysconfig/varnish 的有效參數以下:
NFILES=131072
MEMLOCK=82000
NPROCS="unlimited"
RELOAD_VCL=1
VARNISH_VCL_CONF=/etc/varnish/default.vcl
VARNISH_LISTEN_PORT=80 # 通常修改varnish的監聽端口
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
VARNISH_ADMIN_LISTEN_PORT=6082
VARNISH_SECRET_FILE=/etc/varnish/secret
VARNISH_MIN_THREADS=50
VARNISH_MAX_THREADS=1000
VARNISH_THREAD_TIMEOUT=120
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin
VARNISH_STORAGE_SIZE=1G
VARNISH_STORAGE_MEM_SIZE=128M # 使用malloc存儲方法時的內存大小
VARNISH_STORAGE="malloc,${VARNISH_STORAGE_MEM_SIZE}" # 配置後端存儲方法爲malloc
VARNISH_TTL=120
DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \
-f ${VARNISH_VCL_CONF} \
-T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \
-t ${VARNISH_TTL} \
-w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \
-u varnish -g varnish \
-S ${VARNISH_SECRET_FILE} \
-s ${VARNISH_STORAGE}"
配置vcl的相關參數:
# 修改 /etc/varnish/default.vcl 中如下內容:
backend default {
.host = "172.16.10.11";
.port = "80";
}
此時咱們訪問:http://172.16.10.77時,會發現第一次響應速度比較慢,隨後的響應是很快的。
此時,能夠經過varnishstat查看緩存命中狀況。
也能夠自行定義緩存策略:
Varnish內置變量:
請求到達時可用的內置變量:
req.url
req.request
req.http.HEADER
req.restarts: 請求被重啓的次數;
server.ip
server.port
server.hostname
client.ip
req.backend
向後後端主機請求時可用的內置變量
bereq.url
bereq.request
bereq.http.HEADER
bereq.connect_timeout
bereq.proto
從後端主機獲取到響應的object時可用的內置變量
beresp.status
beresp.response
beresp.http.HEADER
beresp.ttl
beresp.backend.name
beresp.backend.ip
beresp.backend.port
緩存對象進入緩存時可用的內置變量(只能用於vcl_hit或vcl_error,且大多爲只讀)
obj.status
obj.response
obj.ttl
obj.hits
obj.http.HEADER
響應給客戶端時可用的內置變量
resp.proto
resp.status
resp.response
resp.http.HEADER
自定義web.vcl,內容以下:
[root@varnish ~]# cat /etc/varnish/web.vcl
# 定義後端主機,並提供健康狀態檢測
backend web1 {
.host = "172.16.10.11";
.probe = {
.url="/index.html";
.interval=2s;
.window=8;
.threshold=2;
}
}
backend web2 {
.host = "172.16.10.16";
.probe = {
.url="/index.html";
.interval=2s;
.window=8;
.threshold=2;
}
}
director websrv round-robin {
{ .backend = web1; }
{ .backend = web2; }
}
# 定義一個acl 目的是進行緩存裁剪
acl purgers {
"127.0.0.1";
"172.16.0.0"/16;
}
sub vcl_recv {
set req.backend = websrv;
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;
}
}
# 定義那些內容不緩存的
if (req.url ~ "^/test.html$") {
return(pass);
}
if (req.request == "PURGE") {
if (!client.ip ~ purgers) {
error 405 "Method not allowed";
}
return (lookup);
}
if (req.request != "GET" &&
req.request != "HEAD" &&
req.request != "PUT" &&
req.request != "POST" &&
req.request != "TRACE" &&
req.request != "OPTIONS" &&
req.request != "DELETE") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
if (req.request != "GET" && req.request != "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 (lookup);
}
sub vcl_pipe {
return (pipe);
}
# sub vcl_hash {
# hash_data(req.url);
# if (req.http.host) {
# hash_data(req.http.host);
# } else {
# hash_data(server.ip);
# }
# return (hash);
# }
sub vcl_hit {
if (req.request == "PURGE") {
purge;
error 200 "Purged";
}
return (deliver);
}
sub vcl_miss {
if (req.request == "PURGE") {
purge;
error 404 "Not in cache";
}
return (fetch);
}
sub vcl_pass {
if (req.request == "PURGE") {
error 502 "PURGE on a passed object";
}
return (pass);
}
#
# sub vcl_fetch {
# if (beresp.ttl <= 0s ||
# beresp.http.Set-Cookie ||
# beresp.http.Vary == "*") {
# /*
# * Mark as "Hit-For-Pass" for the next 2 minutes
# */
# set beresp.ttl = 120 s;
# return (hit_for_pass);
# }
# return (deliver);
# }
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 Via" + " " + server.hostname;
} else {
set resp.http.X-Cache = "MISS Via" + " " + server.hostname;
}
return (deliver);
}
# sub vcl_deliver {
#
#
# return (deliver);
# }
#
# sub vcl_error {
# set obj.http.Content-Type = "text/html; charset=utf-8";
# set obj.http.Retry-After = "5";
# synthetic {"
# <?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>"} + obj.status + " " + obj.response + {"</title>
# </head>
# <body>
# <h1>Error "} + obj.status + " " + obj.response + {"</h1>
#"} + obj.response + {"# Guru Meditation:
#XID: "} + req.xid + {"# <hr>
#Varnish cache server# </body>
# </html>
# "};
# return (deliver);
# }
#
sub vcl_init {
return (ok);
}
sub vcl_fini {
return (ok);
}
vcl自動編譯的方法,編輯 /etc/sysconfig/varnish 文件中的:
VARNISH_VCL_CONF=/etc/varnish/web.vcl