高性能HTTP加速器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。當中包含Facebook。
特性:
- VCL(Varnish Configuration Language):差別於其它系統。Varnish採用了自身的配置語言來配置,很easy上手,這些配置會被編譯成二進制機器碼,明顯加快了運行速度。
- Health checks:完好的健康檢查機制。
- ESI(Edge Side Includes):在HTML中嵌入動態腳本文件。
- Directors:後端server的調度方式:random,round-robin。client,hash,DNS。
- Purging and banning:強大的緩存清除功能。能夠以正則表達式的形式清除緩存。
- Logging
in Varnish:Varnish的log不是記錄在文件裏的,而是記錄在共享內存中。
當日誌大小達到分配的共享內存容量,覆蓋掉舊的日誌。以這樣的方式記錄日誌比文件的形式要快非常多,而且不須要磁盤空間。
- 豐富的管理程序:varnishadm,varnishtop,varnishhist,varnishstat以及varnishlog等。
環境:CentOS 6.5
首先安裝ncurses-devel,否則varnishstat,varnishtop都無法編譯完畢
yum install ncurses-devel安裝Varnish
wget https://repo.varnish-cache.org/source/varnish-4.0.1.tar.gz tar -zxvf varnish-4.0.1.tar.gz cd varnish-4.0.1 ./configure --prefix=/usr/local/varnish make && make install
開啟Varnish
/usr/local/varnish-2.1.5/sbin/varnishd -f /usr/local/varnish-2.1.5/etc/varnish/default.vcl -T 127.0.0.1:2000 -a 0.0.0.0:80 -s file,/tmp,200M當中
-f
用來指定配置文件,-T
指定管理臺的訪問地址。-a
指定Varnish監聽地址,-s
指定Varnish以文件方式來緩存資源,地址為/tmp,大小200MB。可能會報的錯:
Message from VCC-compiler: No backends or directors found in VCL program, at least one is necessary. Running VCC-compiler failed, exit 1 VCL compilation failed解決的方法:沒有設置varnish配置文件
配置文件(能夠配置多個後端處理器,以輪詢方式實現負載均衡)
backend default { .host = "127.0.0.1"; .port = "8080"; .connect_timeout = 5s; .first_byte_timeout= 5s; .probe = { #health check .url = "/check.txt"; .interval = 5s; .timeout = 5s; .window = 5; .threshold = 3; } } sub vcl_recv { 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.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 { # Note that only the first request to the backend will have # X-Forwarded-For set. If you use X-Forwarded-For and want to # have it set for all requests, make sure to have: # set bereq.http.connection = "close"; # here. It is not set by default as it might break some broken web # applications, like IIS with NTLM authentication. return (pipe); } sub vcl_pass { return (pass); } sub vcl_hash { set req.hash += req.url; if (req.http.host) { set req.hash += req.http.host; } else { set req.hash += server.ip; } return (hash); } sub vcl_hit { if (!obj.cacheable) { return (pass); } return (deliver); } sub vcl_miss { return (fetch); } sub vcl_fetch { if (!beresp.cacheable) { return (pass); } if (beresp.http.Set-Cookie) { return (pass); } return (deliver); } sub vcl_deliver { return (deliver); } sub vcl_error { set obj.http.Content-Type = "text/html; charset=utf-8"; 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> <p>"} obj.response {"</p> <h3>Guru Meditation:</h3> <p>XID: "} req.xid {"</p> <hr> <p>Varnish cache server</p> </body> </html> "}; return (deliver); }
啟動腳本
wget -O varnishd https://raw.github.com/gist/3671408/3a51578bbd60a4cf8317bdc9508527b81eb23da5/varnishd cp varnishd /etc/init.d/varnishd chmod +x /etc/init.d/varnishd /etc/init.d/varnishd start
Subroutine列表
-
vcl_recv 在請求開始時候被調用,在請求已經被接收到而且解析後調用。
目的就是決定是否處理這個請求,怎麽處理,使用哪個後端。vcl_recv以
return
結束,參數能夠為例如以下keyword:
error code [reason]:返回錯誤碼給client,丟棄請求。
pass:轉換到pass模式。控制權最後會轉移到
vcl_pass
。
pipe:轉換到pipe模式。控制權最後會轉移到vcl_pipe
。
lookup:在緩存中尋找請求對象。控制權最後會轉移到
vcl_hit
或者vcl_miss
。決定於對象是否在緩存中。 -
vcl_pipe 當進入pipe模式的時候被調用。
在這個模式中,請求會被轉移到後端。興許的數據無論是從client還是後端來的都會以不變的方式傳送,直到連接關閉為止。vcl_pipe以
return
結束,參數能夠為例如以下keyword:
error code [reason]:返回錯誤碼給client,丟棄請求。
pipe:以pipe模式運行。 -
vcl_pass 當進入pass模式的時候會被調用。在這個模式中,請求會被傳送到後端,然後後端的響應會被傳送回client,可是響應不會進入緩存中。接下來通過同樣client連接發起的請求會以普通的方式來處理。vcl_pass以
return
結束。參數能夠為例如以下keyword:
error code [reason]:返回錯誤碼給client。丟棄請求。
pass:以pass模式運行。
restart:又一次啟動這個事務。添加了重新啟動計數。假設重新啟動的次數高於
max_restarts
,varnish會引起一個錯誤。 -
vcl_hash 你假設把想把數據增加到hash中,那麽調用hash_data()。vcl_hash以
return
結束,參數能夠為例如以下keyword:
hash:運行hash邏輯。 -
vcl_hit 假設請求的對象在緩存中被找到了,那麽在緩存查找結束後被調用。vcl_hit以
return
結束。參數能夠為例如以下keyword:
deliver:deliver緩存對象到client。控制權最後會轉移到vcl_deliver
。
error code [reason]:返回錯誤碼給client,丟棄請求。
pass:切換到pass模式。控制權最後會轉移到
vcl_pass
。
restart:又一次啟動這個事務。添加了重新啟動計數。假設重新啟動的次數高於
max_restarts
,varnish會引起一個錯誤。 -
vcl_miss 假設請求的對象在緩存中沒有被找到,那麽在緩存查找結束後被調用。
目的是為了決定是否去後端獲取這個請求對象,而且要選擇哪個後端。vcl_miss以return結束,參數能夠為例如以下keyword:
error code [reason]:返回錯誤碼給client,丟棄請求。
pass:切換到pass模式。控制權最後會轉移到
vcl_pass
。
fetch:去後端獲取請求對象。控制權最後會轉移到vcl_fetch
。 -
vcl_fetch 當一個對象被成功從後端獲取的時候此方法會被調用。
vcl_fetch以
return
結束,參數能夠為例如以下keyword:
deliver:可能把對象放入緩存中,然後再deliver到client。控制權最後會轉移到
vcl_deliver
。
error code [reason]:返回錯誤碼給client,丟棄請求。
esi:以ESI形式來處理剛剛被獲取到的對象。
pass:切換到pass模式。控制權最後會轉移到vcl_pass
。
restart:又一次啟動這個事務。添加了重新啟動計數。假設重新啟動的次數高於
max_restarts
。varnish會引起一個錯誤。 -
vcl_deliver當一個緩存的對象被deliver到client的時候,此方法會被調用。vcl_deliver以
return
結束,參數能夠為例如以下keyword:
deliver:發送對象到client。
error code [reason]:返回錯誤碼給client,丟棄請求。
restart:又一次啟動這個事務,添加重新啟動計數。假設重新啟動的次數高於max_restarts
,varnish會引起一個錯誤。 -
vcl_error 當遇見一個錯誤的時候會被調用,錯誤可能是跟後端有關系或者內部錯誤。vcl_error以
return
結束,參數能夠為例如以下keyword:
deliver:發送對象到client。
restart:又一次啟動這個事務,添加重新啟動計數。假設重新啟動的次數高於max_restarts
。varnish會引起一個錯誤。
重要變量
subroutine不帶參數,一般通過全局變量來實現信息的傳遞。
例如以下變量在backend中有效:
- .host:backend的主機名或者IP。
- .port:backend的端口。
例如以下變量在處理一個請求(比如vcl_recv
)的時候可用:
- client.ip:客戶端IP地址。
- server.hostname:server的主機名。
- server.identity:server標示,當啟動varnish的時候用
-i
參數來指定。假設varnish啟動時候沒有指定
-i
參數,那麽server.identity會被設置為用-n
參數所指定的實例名稱。 - server.ip:serverIP地址。
- server.port:server端口。
- req.request:請求類型(比如
GET
,HEAD
)。 - req.url:請求的URL。
- req.proto:HTTP協議版本號。
- req.backend:處理請求的後端server。
- req.backend.healthy:後端是否健康。
health check須要在
backend
的probe
中進行設置。 - req.http.header:相關的HTTP頭。
- req.hash_always_miss:強迫對於本次請求的緩存查找結果為miss。假設設置為
true
,那麽varnish將會忽略不論什麽存在的緩存對象,一直從後端又一次獲取資源。 - req.hash_ignore_busy:在緩存查找時候忽略不論什麽忙的對象。
假設有兩個server,彼此互相查找緩存內容,那麽能夠使用這個變量來避免潛在的死鎖。
例如以下變量在準備一個後端請求(比方在cache miss
或者pass
,pipe
模式)的時候可用:
- bereq.request:請求的類型(比方
GET
,HEAD
)。 - bereq.url:請求的URL。
- bereq.proto:與後端server交互的HTTP協議版本號。
- bereq.http.header:相關的HTTP頭。
- bereq.connect_timeout:與後端連接的超時時間。
- bereq.first_byte_timeout:從後端返回第一個字節所需等待的秒數,在
pipe
模式中不可用。 - bereq.between_bytes_timeout:從後端返回的每一個字節之間的時間間隔,以秒計。
在
pipe
模式中不可用。
例如以下的變量在請求對象從後端返回之後,在其被放入緩存之前可用。換句話說,也就是在vcl_fetch
中可用。
- beresp.proto:HTTP協議版本號。
- beresp.status:後端返回的HTTP狀態碼(比如200,302等)。
- beresp.response:後端返回的狀態內容(比如
OK
,Found
)。 - beresp.cacheable:假設請求的結果是能夠被緩存的,那麽此變量為
true
。假設HTTP狀態碼為200, 203, 300, 301, 302, 404,410之中的一個而且pass
沒有在vcl_recv
中被調用。那麽這個結果就是能夠被緩存的。假設response的
TTL
和grace time
都為0,那麽beresp.cacheable
將會為0。beresp.cacheable
是可寫的。 - beresp.ttl:緩存對象的生存時間,以秒為單位。這個變量是可寫的。
在對象已經存在於緩存中並被查詢到的時候。一般在vcl_hit
和vcl_deliver
中。例如以下的變量(大部分是read-only)可用:
- obj.proto:與後端交互的HTTP版本號協議。
- obj.status:後端返回的HTTP狀態碼。
- obj.response:後端返回的HTTP狀態內容。
- obj.cacheable:假設對象的beresp.cacheable為
true
。那麽此變量的值為true
。除非你強制delivery,否則obj.cacheable
一直為true
。 - obj.ttl:緩存對象的生存時間。以秒為單位。這個變量是可寫的。
- obj.lastuse:從如今到對象近期一次訪問所間隔的時間。以秒為單位。
- obj.hits:對象被發送到client的次數,0表示緩存查詢miss了。
例如以下變量在決定對象hash key的時候可用:
- req.hash:hash key被用來關聯一個緩存中的對象。
在讀寫緩存的時候都會被用到。
例如以下變量在準備把一個響應發送給client時候可用:
- resp.proto:響應使用的HTTP協議版本號。
- resp.status:將要返回的HTTP狀態碼。
- resp.response:將要返回的HTTP狀態內容。
- resp.http.header:相關的HTTP頭。
高性能HTTP加速器Varnish安裝與配置(包含常見錯誤)