一、Varnish 簡介
Varnish是一款高性能的開源HTTP加速器,挪威最大的在線報紙 Verdens Gang 使用3臺Varnish代替了原來的12臺Squid,性能比以前更好。
Varnish 的作者Poul-Henning Kamp是FreeBSD的內核開發者之一,他認爲現在的計算機比起1975年已經複雜許多。在1975年時,儲存媒介只有兩種:內存與硬盤。但現在計算 機系統的內存除了主存外,還包括了CPU內的L1、L2,甚至有L3快取。硬盤上也有自己的快取裝置,因此Squid Cache自行處理物件替換的架構不可能得知這些情況而做到最佳化,但操作系統可以得知這些情況,所以這部份的工作應該交給操作系統處理,這就是 Varnish cache設計架構。
Varnish 項目是2006年發佈的第一個版本0.9.距今已經八年多了,此文檔之前也提過varnish還不穩定,那是2007年時候編寫的,經過varnish開 發團隊和網友們的辛苦耕耘,現在的varnish已經很健壯。很多門戶網站已經部署了varnish,並且反應都很好,甚至反應比squid還穩定,且效 率更高,資源佔用更少。相信在反向代理,web加速方面,varnish已經有足夠能力代替squid。
二、Varnish狀態引擎
Receive狀態,也就是請求處理的入口狀態,根據VCL規則判斷該請求應該Pass或Pipe,還是進入Lookup(本地查詢)。
Lookup狀態,進入此狀態後,會在hash表中查找數據,若找到,則進入Hit狀態,否則進入miss狀態。
Pass狀態,在此狀態下,會進入後端請求,即進入Fetch狀態。
Fetch狀態,在Fetch狀態下,對請求進行後端獲取,發送請求,獲得數據,是否進行本地存儲。
Deliver狀態, 將獲取到的數據發送給客戶端,然後完成本次請求。
pipe狀態時,請求直接傳遞至後端主機,在請求和返回的內容沒有改變的情況下,將不變的內容返回給客戶端,直到這個鏈接被關閉。
三、內置公用變量
VCL內置的公用變量可以用在不同的VCL函數中。下面根據這些公用變量使用的不同階段依次進行介紹。
(1).當請求到達後,可以使用的公用變量
req.backend 指定對應的後端主機
server.ip 表示服務器端IP
client.ip 表示客戶端IP
req.request 指定請求的類型,例如GET、HEAD和POST等
req.url 指定請求的地址
req.proto 表示客戶端發起請求的HTTP協議版本
req.http.header 表示對應請求中的HTTP頭部信息
req. restarts ;/.l表示請求重啓的次數,默認最大值爲4
(2).Varnish在向後端主機請求時,可以使用的公用變量
beresp.request 指定請求的類型,例如GET合HEAD等
beresp.url 指定請求的地址
beresp .proto 表示客戶端發起請求的HTTP協議版本
beresp .http.header 表示對應請求中的HTTP頭部信息
beresp .ttl 表示緩存的生存週期,也就是cache保留多長時間,單位是秒
(3).從cache或後端主機獲取內容後,可以使用的公用變量
obj.status 表示返回內容的請求狀態代碼,例如200、302和504等
obj.cacheable 表示返回的內容是否可以緩存,也就是說,如果HTTP返回的是200、203、300、301、302、404或410等,並且有非0的生存期,則可以緩存
obj.valid 表示是否是有效的HTTP應答
obj.response 表示返回內容的請求狀態信息
obj.proto 表示返回內容的HTTP協議版本
obj.ttl 表示返回內容的生存週期,也就是緩存時間,單位是秒
obj.lastuse 表示返回上一次請求到現在的間隔時間,單位是秒
(4).對客戶端應答時,可以使用的公用變量
resp.status 表示返回給客戶端的HTTP狀態代碼
resp.proto 表示返回給客戶端的HTTP協議版本
resp.http.header 表示返回給客戶端的HTTP頭部信息
resp.response 表示返回給客戶端的HTTP狀態信息
在上面的講述中,只介紹了常用的VCL內置公用變量,如果需要了解和使用更多的公用變量信息,請登錄varnish官方網站查閱。https://www.varnish-cache.org/docs/3.0/
四、安裝與基本配置
直接去官網下載rpm包安裝即可。
編輯啓動腳本配置文件 /etc/sysconfig/varnish
NFILES=131072 //定義最大打開文件的數量
MEMLOCK=82000 //定義用於緩存日誌的空間 (KB)
NPROCS="unlimited" //最大打開的線程數量unlimited(無上限)
RELOAD_VCL=1 //無需重啓服務,可以重新編譯載入VCL
VARNISH_VCL_CONF=/etc/varnish/default.vcl //規則文件
VARNISH_LISTEN_PORT=80 //服務偵聽端口
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_MEMORY_SIZE=64M //使用緩存的內存大小
VARNISH_STORAGE="malloc,${VARNISH_MEMORY_SIZE}" //指定緩存類型,這是是指使用內存緩存。也可以用file指定緩存至磁盤文件上。
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}"
啓動varnish
/etc/init.d/varnish start
連接管理端口,查看管理命令。
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
五、配置一個簡單的後端
vim /etc/varnish/default.vcl
backend default { //定義後端主機。
.host = "192.168.18.201";
.port = "80";
}
sub vcl_recv { //在recv調用。
set req.backend = default;
}
加載配置文件,使用配置。
varnishadm
vcl.load d2 /etc/varnish/default.vcl
vcl.use d2
測試一下
已成功代理至後端服務器,但是我們無法查看是否命中,接下來添加配置。
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-cache = "Hit from" +" "+server.ip;
} else {
set resp.http.X-cache = "Miss var"+" "+server.ip;
}
}
重新載入文件和上述方法相同(略)
測試效果
現在配置不讓它緩存
backend default {
.host = "192.168.18.201";
.port = "80";
}
sub vcl_recv {
if (req.url ~ "\.jsp$") {
return(pass);
}
set req.backend = default;
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-cache = "Hit from" +" "+server.ip;
} else {
set resp.http.X-cache = "Miss var"+" "+server.ip;
}
}
控制某個目錄下的文件不讓緩存
backend default {
.host = "192.168.18.201";
.port = "80";
}
sub vcl_recv {
if (req.url ~ "^/bbs/"
) {
return(pass);
}
set req.backend = default;
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-cache = "Hit from" +" "+server.ip;
} else {
set resp.http.X-cache = "Miss var"+" "+server.ip;
}
}
上面的所有配置都是根據用戶請求的內容做出決策的,下面我們來配置根據響應的內容做出決策。
定義緩存TTL時間
sub vcl_fetch {
if
(req.request ==
"GET"
&& req.url ~
"\.(html|css|js|jpg|jpeg|png|gif)$"
) {
set
beresp.ttl = 3600s;
}
}
六、兩臺WEB服務器動靜分離實例
backend web1 {
.host = "192.168.18.201";
.port = "80";
}
backend web2 {
.host = "192.168.18.202";
.port = "80";
}
sub vcl_recv {
if (req.url ~ "\.jsp$") {
set req.backend = web2;
} else {
set req.backend = web1;
}
}
sub vcl_fetch {
if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg|css|js|bmp|ico|txt)$") {
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 var"+" "+server.ip;
}
}
七、配置健康狀態檢測
.url //指定檢測的URL
.request 探測後端主機健康狀態時所請求內容的詳細格式,定義後,它會替換.url指定的探測方式;比如:
.request =
"GET /.healthtest.html HTTP/1.1"
"Host: www.test.com"
"Connection: close"
;
.window //做健康狀況檢測時,至少要採樣多少次;
.threshold //在.window中指定的次數中,至少有多少次是成功的才判定後端主機正健康運行;默認是3;
.initial //Varnish啓動時對後端主機至少需要多少次的成功探測,默認同.threshold;
.expected_response //期望後端主機響應的狀態碼,默認爲200;
.interval //探測請求的發送頻率,默認爲5秒;
.timeout //每次探測請求的過期時長,默認爲2秒;
有兩種定義方式:
1、在backend中使用.probe 定義,例:
backend www {
.host = "www.tuchao.com";
.port = "http";
.probe = {
.url = "/test.jpg";
.timeout = 1s;
.window = 8;
.threshold = 3;
.initial = 3;
}
}
2、可以明確的定義一個公共的probe,在backend中用.probe調用。例:
probe healthchk {
.url = "/health.html";
.interval = 2s;
.timeout = 2s;
.expected_response = 200;
.window = 3;
.threshold = 1;
}
backend web1 {
.host = "192.168.18.201";
.port = "80";
.probe = healthchk;
}
backend web2 {
.host = "192.168.18.202";
.port = "80";
.probe = healthchk;
}
sub vcl_recv {
if (req.url ~ "\.jsp$") {
set req.backend = web2;
} else {
set req.backend = web1;
}
}
sub vcl_fetch {
if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg|css|js|bmp|ico|txt)$") {
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 var"+" "+server.ip;
}
}
八、負載均衡的實現
調度算法:
random //根據權重隨機調度
hash //將同一個請求發往同一個主機
配置實例:
probe healthchk {
.url = "/health.html";
.interval = 2s;
.timeout = 2s;
.expected_response = 200;
.window = 3;
.threshold = 1;
}
backend web1 {
.host = "192.168.18.201";
.port = "80";
.probe = healthchk;
}
backend web2 {
.host = "192.168.18.202";
.port = "80";
.probe = healthchk;
}
director webs random { //定義集羣,定義調度算法。
.retries = 2;
{
.backend = web1;
.weight = 1;
}
{
.backend = web2;
.weight = 1;
}
}
sub vcl_recv {
set req.backend = webs;
}
sub vcl_fetch {
if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg|css|js|bmp|ico|txt)$") {
set beresp.ttl = 3600s;
}
}
九、移除單個緩存對象配置實例
probe healthchk {
.url = "/health.html";
.interval = 2s;
.timeout = 2s;
.expected_response = 200;
.window = 3;
.threshold = 1;
}
acl purgers {
"127.0.0.1";
"192.168.18.0"/24;
}
backend web1 {
.host = "192.168.18.201";
.port = "80";
.probe = healthchk;
}
backend web2 {
.host = "192.168.18.202";
.port = "80";
.probe = healthchk;
}
director webs hash {
.retries = 2;
{
.backend = web1;
.weight = 1;
}
{
.backend = web2;
.weight = 1;
}
}
sub vcl_recv {
if (req.request == "PURGE") {
if (!client.ip ~ purgers) {
error 405 "Method not allow";
}
return (lookup);
}
set req.backend = webs;
}
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 502 "Purged on a passed object.";
}
}
測試效果
先訪問一下
這時執行PURGE清除
再訪問一下
看,Miss,緩存被成功清除。
十、防盜鏈配置實例
sub vcl_recv {
if (req.http.referer ~ "http://.*" ) {
if (!(req.http.referer ~ "http://.*\.tuchao\.com" || req.http.referer ~ "http://.*\.google\.com.*" || req.http.referer ~ "http://.*\.baidu\.com.*" )) {
set req.http.host = "www.tuchao.com";
set req.url = "/referer/tuchao.html";
}
}
if (req.request == "PURGE") {
if (!client.ip ~ purgers) {
error 405 "Method not allow";
}
return (lookup);
}
set req.backend = webs;
}
測試結果
這篇就寫到這裏了,總共分了十個章節,希望大家能從中學習到知識。
我掌握的也不牢靠,歡迎大家與我交流QQ1183710107.