緩存能分擔許多web訪問的壓力,緩存爲王的今天,緩存猶爲重要.
varnish 是一款非常輕量級,也很強大的一款提供緩存服務的應用.
varnish的配置是通過VCL緩存策略工具實現的,這個工具是一種簡單的編程語言,用戶可以自定義變量、
有好幾個內置的函數和變量,但是它的函數不支持接受參數,而且沒有返回值。使用VCL編寫的緩存策略通常保存至.vcl文件中,
其需要編譯成二進制的格式後才能由varnish調用。事實上,整個緩存策略就是由幾個特定的子例程如vcl_recv、
vcl_fetch等組成,它們分別在不同的位置(或時間)執行,如果沒有事先爲某個位置自定義子例程,varnish將會執行默認的定義。
VCL策略在啓用前,會由management進程將其轉換爲C代碼,而後再由gcc編譯器將C代碼編譯成二進制程序,
所以varnish的安裝和運行依賴於gcc庫。編譯完成後,management負責將其連接至varnish實例,
即子進程。編譯時會檢查語法是否有誤,避免了裝載錯誤語法的VCL。一但編譯完成並且沒有語法錯誤就會被裝載,
同時可以保存好幾份配置,當你覺得之前的配置策略更科學時,調用之前的配置即可. 只要調用庫中的配置策略,就可以使規則生效,
無需重啓或者reload.所以修改配置策略的代價很小。配置的策略只有在varnish重啓時纔會清除,當然,也可以手動清理,
可以使用varnishadm的vcl.discard命令完成。
varnish支持多種不同類型的後端存儲,這可以在varnishd啓動時使用-s選項指定。後端存儲的類型包括:
(1)file:使用特定的文件存儲全部的緩存數據,並通過操作系統的mmap()系統調用將整個緩存文件映射至內存區域(如果內存夠大,條件允許); 可以指定其所保存的位置,大小,以及緩存分配粒度, 即每次分配的大小,直到size大小爲止不再分配.使用方法 file[,path[,size[,granularity]]]
(2)malloc:使用malloc()庫調用在varnish啓動時向操作系統申請指定大小的內存空間以存儲緩存對象; 使用方法malloc[,size]
(3)persistent(experimental):與file的功能相同,但可以持久存儲數據(即重啓varnish數據時不會被清除);仍處於測試期,悲催,好不容易有一個能持久保存的,還不穩定;
varnish的進程分兩類,
一, 管理進程, (master進程),其工作職責如下
1,讀入配置文件
2,調用合適的存儲類型 ,varnish支持將緩存寫入磁盤
3,創建/讀入相應大小的緩存文件 (但是這個功能還處於測試階段,建議暫時不要使用)
4,初始化管理,將緩存文件結構空間關聯到存儲結構體 ,以待分配,
5,fork出多個空閒子進程並監控各child進程
二,工作進程 (child子進程)
1,將前面打開的存儲文件整個mmap到內存
2,創建並實始化空閒的結構體,用來存儲緩存對象,
3,由諸多線程各司其職負責完成相關的工作:
主進程 fork 子進程,主進程等待子進程的信號,子進程退出後,主進程重新啓動子進程,子進程生成若干線程。
Accept 線程:接受請求,將請求分配到空閒子進程上,並讓空閒的work線程響應用戶請求
Work線程: work線程有多個,從隊列領取請求,並對請求處理,處理完成後,繼續領取下一個請求進行處理
work線程處理時,會讀取該請求的url, 以此判定本地緩存中是否有該緩存對象命中,如果命中直接構建響應報文,
如果沒有,則去上游服務器查找數據,並緩存至本地再構建響應報文響應請求.
Epoll 線程: 一個請求處理稱作一個 session,在 session 週期內,處理完請求後,會交給 Epoll 處理,監聽是否還有事件發生。
Expire 線程:對於緩存的對象,根據過期時間,組織成二叉堆,該線程週期檢查該堆的根,處理過期的文件。
線程之間的關係:
worker:處理用戶請求
accept: 接收用戶請求
當緩存空間耗盡:
需要清理緩存空間了,可以使用LRU算法清理,(LRU指最近最少使用的)
查看一下varnish的內置函數
1、vcl_recv函數
用於接收和處理請求,當請求到達併成功接收後被調用,通過判斷請求的數據來決定如何處理請求。
此函數一般以如下幾個關鍵字結束:
pass:表示進入pass模式,把請求控制權交給vcl_pass函數。
pipe:表示進入pipe模式,把請求控制權交給vcl_pipe函數。
lookup: 表示進入hash,把請求控制權交給vcl_hash函數.
error code [reason]:表示返回“code”給客戶端,並放棄處理該請求,“code”是錯誤標識,例如200、405等,“reason”是錯誤提示信息。
2、vcl_pipe函數
此函數在進入pipe模式時被調用,用於將請求直接傳遞至後端主機,在請求和返回的內容沒有改變的情況下,將不變的內容返回給客戶端,直到這個鏈接關閉
此函數一般以如下幾個關鍵字結束:
error code [reason]
pipe
3、vcl_pass函數
此函數在進入pass模式時被調用,用於將請求直接傳遞至後端主機,後端主機應答數據後送給客戶端,但不進行任何緩存,在當前連接下每次都返回最新的內容, 關鍵字結束:
error code [reason]
pass
4、lookup
表示在緩存裏查找被請求的對象,並且根據查找的結果把控制權交給函數vcl_hit或者函數vcl_miss
5、vcl_hit函數
在執行lookup指令後,如果在緩存中找到請求的內容,將自動調用該函數
此函數一般以如下幾個關鍵字結束:
deliver:表示將找到的內容發送給客戶端,並把控制權交給函數vcl_deliver
error code [reason]
pass
6、vcl_miss函數
在執行lookup指令後,如果沒有在緩存中找到請求的內容時自動調用該方法,此函數可以用於判斷是否需要從後端服務器取內容
此函數一般以如下幾個關鍵字結束:
fetch:表示從後端獲取請求的內容,並把控制權交給vcl_fetch函數
error code [reason]
pass :去後端主機取數據時,額外再做一些操作
7、vcl_fetch函數
在從後端主機更新緩存並且獲取內容後調用該方法,接着,通過判斷獲取的內容來決定是否將內容放入緩存,還是直接返回給客戶端
此函數一般以如下幾個關鍵字結束:
error code [reason]
pass 可以不緩存
deliver 也可以緩存
8、vcl_deliver函數
在緩存中找到請求的內容後,發送給客戶端前調用此方法。此函數一般以如下幾個關鍵字結束:
error code [reason]
deliver 響應客戶端請求
9、vcl_timeout 函數
此函數在緩存內容到期前調用。一般以如下幾個關鍵字結束:
discard:從緩存中清除該內容。
fetch 也可以去後端主機取數據
10、vcl_discard函數
在緩存內容到期後或緩存空間不夠時,自動調用該方法,一般以如下幾個關鍵字結束:
keep:表示將內容繼續保留在緩存中
discard:從緩存中清除該內容。
varnish的配置文件:vcl
用於定義後端節點
取緩存對象
是否緩存
附,圖1 ==>varnish工作機制圖解
varnish 自己監聽在哪個端口,如何緩存等,是通過varnish的命令行接口參數來定義的
[root@localhost~]# vim /etc/sysconfig/varnish => 查看配置文件
其配置文件默認是使用 Alternative 3, Advanced configuration高級模式配置的.
NFILES=131072 => 所能夠打開的最大文件數 MEMLOCK=82000 => 用多大內存空間保存日誌信息 DAEMON_COREFILE_LIMIT="unlimited" => 進程核心轉儲所使用的內存空間,unlimited表示無上限 RELOAD_VCL=1 => 重新啓動服務時是否重新讀取VCL並重新編譯的 VARNISH_VCL_CONF=/etc/varnish/default.vcl => 默認讀取的VCL文件 VARNISH_LISTEN_PORT=80 => 監聽的端口,默認監聽6081 VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 => 管理接口監聽的地址 VARNISH_ADMIN_LISTEN_PORT=6082 => 管理接口監聽的端口 ,這個管理接口可以控制varnish的工作模式和特性 VARNISH_SECRET_FILE=/etc/varnish/secret => 使用的密鑰文件 VARNISH_MIN_THREADS=1 => 最少線程數 VARNISH_MAX_THREADS=1000 => 最大線程數 VARNISH_THREAD_TIMEOUT=120 => 線程的超時時間 VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin => 基於文件存儲時的文件路徑 VARNISH_STORAGE_SIZE=1G => 存儲文件的大小 VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}" => 存儲的文件格式 VARNISH_MALLOC="malloc,128m" =>如,想改成使用malloc的方式存儲內存可以定義一個變量其實也就是定義和調用的關係 ,甚至是可以在後面相關的調用 -s 參數後,直接跟上要修改成的屬性. 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}" => 使用定義的各高級配置的參數 -s ${VARNISH_MALLOC} =>前面紅字定義的,後面調用
有了以上的知識,我們開始配置一個varnish緩存,並高度上游服務器的一種組織架構,拓普圖如下.
[root@localhost~]# vim /etc/sysconfig/varnish
VARNISH_LISTEN_PORT=80 =>修改當前的監聽端口. 其它的參數都使用默認
# service varnish start =>啓動varnish服務.
[root@php5_6 ~]# ps aux|grep varnish
root 2052 0.0 0.3 143684 3476 pts/0 S+ 13:17 0:00 vim /etc/varnish/default.vcl
root 2094 0.0 0.3 143684 3500 pts/1 S+ 13:45 0:00 vim /etc/sysconfig/varnish
root 2214 0.0 0.1 112300 1192 ? Ss 14:22 0:00 /usr/sbin/varnishd -P /var/run/varnish.pid -a :80 -f /etc/varnish/default.vcl -T 127.0.0.1:6082 -t 120 -w 50,1000,120 -u varnish -g varnish -S /etc/varnish/secret -s file,/var/lib/varnish/varnish_storage.bin,1G
varnish 2215 0.7 0.4 2273404 4572 ? Sl 14:22 0:01 /usr/sbin/varnishd -P /var/run/varnish.pid -a :80 -f /etc/varnish/default.vcl -T 127.0.0.1:6082 -t 120 -w 50,1000,120 -u varnish -g varnish -S /etc/varnish/secret -s file,/var/lib/varnish/varnish_storage.bin,1G =>可以看到, varnish的緩存文件存放的位置以及大小
root 2364 0.0 0.0 103252 824 pts/3 S+ 14:26 0:00 grep varnish
# cp /etc/varnish/default.vcl /etc/varnish/cl1.vcl =>將默認的vcl配置拷貝一份,
# vim /etc/varnish/cl1.vcl =>編輯策略一這個配置文件
backend default { .host = "172.16.26.6"; .port = "80"; } 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; 如果有 req.http.X-Forwarded-For信息,爲其添加上請求的客戶端地址 } else { set req.http.X-Forwarded-For = client.ip; 沒有req.http.X-Forwarded-For信息,將其信息內容改爲請求的客戶端地址 } } 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); =>如果不滿足以上的所有請求方法,無法識別,直接交給pipe處理,控制權轉交給vcl_pipe,直接到上游服務器去了 } if (req.request != "GET" && req.request != "HEAD") { /* We only deal with GET and HEAD by default */ return (pass); =>如果非GET, HEAD請求方式,說明請求的信息無需要緩存,直接交給pass,控制權轉交給 vcl_pass處理 } if (req.http.Authorization || req.http.Cookie) { /* Not cacheable by default */ return (pass); =>這和用戶認證相關,也不緩存, 交給pass,控制權轉交給 vcl_pass處理 } return (lookup); =>交給 vcl_hash處理 }
[root@php5_6 tmp]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 =>連接到本機的varnish的管理接口上
200
-----------------------------
Varnish Cache CLI 1.0
-----------------------------
Linux,2.6.32-431.el6.x86_64,x86_64,-sfile,-smalloc,-hcritbit
varnish-3.0.4 revision 9f83e8f
Type 'help' for command list.
Type 'quit' to close CLI session.
varnish> vcl.load cl1 cl1.vcl =>使用vcl.load 來讀取配置好的策略文件, 這個讀取會使用gcc編譯器編譯爲二進制代碼 如果中間的配置有誤,則會報錯,停止編譯
200
VCL compiled.
varnish> vcl.use cl1 裝載vcl應用策略是由management將其連接到子進程中.
200
此時我們嘗試訪問 varnish 緩存服務器,它就會給我們代理到上游服務器上去了.
我們想查看當前使用的緩存策略,可以使用vcl.show 策略名 來查看.
例如: varnish> vcl.show cl1
如果想丟棄該策略,也很簡單,使用另一個vcl策略後,
varnish> vcl.discard cl1 =>用discard命令就可以清除一個策略了
varnish有許多內置的變量, 不同的變量,有自己特定的使用上下文.
有了以上的認識,我們可以定製更多我們所需要的緩存特性了.比如說,我們想知道這次訪問的緩存是否命中,怎麼辦呢?
由以上的varnish工作機制流程圖可以看到, 任務數據的響應都得經過deliver,那麼我們就開啓 vcl_deliver方法,
並由上表得知, deliver方法中可以使用 boj.hits變量
sub vcl_deliver { if (obj.hits > 0) { =>如果訪問命中次數大於0,說明命中了,給其響應報文添加一個http首部, set resp.http.X-Cache = "HIT via" + " " + server.hostname; } else { set resp.http.X-Cache = "MISS via" + " " + server.hostname; } return (deliver); }
重新裝載配置後訪問,在調試器裏能看到,http添加了請求首部 x-cache ,由於是第一次訪問 其值是我們定義的
MISS via php5_6.cc
第二次訪問時,就命中了.
有一些頁面,我們是不希望緩存的,那麼我們又應該怎麼實現呢?
比如說,我們不希望 /test/test.html被緩存, 試試看怎麼實現.
我們可以在接收到用戶請求的開始,就判段要訪問的url,如果滿足條件就直接到上游服務器取數據.
修改配置文件,我們需要在vcl_recv 函數的一開始就判斷url是否包含有相應的路徑.
sub vcl_recv { if (req.url ~ "^/test/test.html$") { =>如果是訪問指定的路徑不走緩存,直接去上游服務器取數據. return(pass); } }
這樣設定了之後,訪問相應的路徑怎麼着也緩存不了了.
req.request 有一種方法,叫作PURGE. 這是一種清理緩存的方法.
我們不能讓所有人來都能清除緩存.所以我們必須要限定只有對應的權限控制列表才能清除緩存.
acl purgers { =>我們定義一個訪問列表, 只有在這個訪問列表內的ip纔有權限清除緩存 "127.0.0.1"; "172.16.26.6"; } sub vcl_recv { =>在此方法一開始就判斷是否是 PURGE 類型的請求 if (req.request == "PURGE") { =>如果是PURGE請求,且客戶端請求的ip不在權限列表內,則響應一個錯誤狀態碼和信息 if (!client.ip ~ purgers) { error 405 "Method not allowed"; } return (lookup); =>去查詢緩存了. } } sub vcl_hit { if (req.request == "PURGE") { =>如果命中了緩存,並且是PURGE請求類型, purge; =>調用清除緩存的 purge方法 error 200 "Purged"; =>用error函數返回一個正確的響應碼,響應信息 } } sub vcl_miss { if (req.request == "PURGE") { purge; error 404 "Not in cache"; =>說明緩存不存在 } } sub vcl_pass { if (req.request == "PURGE") { =>PURGE方法的請求基本上不會到這裏來, error 502 "PURGE on a passed object"; =>萬一到了,響應一個錯誤碼和錯誤信息回去. } }
測試,經過一次訪問後,存在緩存數據了,再次使用 -X 指定 PURGE方法 請求清除緩存,,
因爲請求發起的ip在訪問權限列表中,所以和預想的一樣,請求成功,得到 200的狀態碼,並提示緩存清理成功
varnish還可以探測後端主機的健康狀態,如果探測不到,會把它踢除, 後來探測到了,還可以把它加回來. backend webapache1 { .host = "172.16.26.1"; .probe = { .url = "/.healthtest.html"; .interval = 2s; =>隔幾秒探測一次 .window = 5; =>至少探測次數 .threshold = 2; =>成功幾次則認爲後端主機是OK的 } }
如何使用多個後端主機呢?
backend webapache1 { .host = "172.16.26.1"; .port = "80"; } backend webapache5 { .host = "172.16.26.5"; .port = "80"; } director webservers random { .retries = 5; { .backend = webapache1; .weight = 2; } { .backend = webapache5; .weight = 2; } } sub vcl_recv { set req.backend=webservers; ==>注意,需要在vcl_recv中指定使用的backend爲你定義的 director名稱.這樣纔能有效調用,不然會報錯. }
此時重載vcl配置, 訪問 就可以實現隨機調用上游主機了.
我們還可以在varnish上實現動靜分離
if (req.url ~ ".php$") { =>如果url中,以.php結尾的,讓其使用特定的backedn -> webapache1
set req.backend=webapache1;
return(pass); =>並且不緩存
}