CentOS 6.5 Varnish緩存服務詳解及應用實現

1、varnish的基本介紹
   Varnish 的作者Poul-Henning Kamp是FreeBSD的內核開發者之一,他認爲現在的計算機比起1975年已經複雜許多。在1975年時,儲存媒介只有兩種:內存與硬盤。但現在計算機系統的內存除了主存外,還包括了cpu內的L1、L2,甚至有L3快取。硬盤上也有自己的快取裝置,因此squid cache自行處理物件替換的架構不可能得知這些情況而做到最佳化,但操作系統可以得知這些情況,所以這部份的工作應該交給操作系統處理,這就是 Varnish cache設計架構。

   Varnish與一般服務器軟件類似,就是一個web緩存代理服務器,分爲master(management)進程和child(worker,主要做cache的工作)進程。master進程讀入命令,進行一些初始化,然後fork並監控child進程。child進程分配若干線程進行工作,主要包括一些管理線程和很多woker線程。

   Management進程主要實現應用新的配置、編譯VCL、監控varnish、初始化varnish以及提供一個命令行接口等。Management進程會每隔幾秒鐘探測一下Child進程以判斷其是否正常運行,如果在指定的時長內未得到Child進程的迴應,Management將會重啓此Child進程。

   Child進程包含多種類型的線程,常見的如:
       Acceptor線程:接收新的連接請求並響應;
       Worker線程:child進程會爲每個會話啓動一個worker線程,因此,在高併發的場景中可能會出現數百個worker線程甚至更多;
       Expiry線程:從緩存中清理過期內容;

Varnish依賴“工作區(workspace)”以降低線程在申請或修改內存時出現競爭的可能性。在varnish內部有多種不同的工作區,其中最關鍵的當屬用於管理會話數據的session工作區。

   進程的工作過程原理及過程:

   wKioL1N3LNmBY7lcAAJafMlNjYg162.jpg

2、varnish與squid的區別
   varnish和squid在中小規模的應用上,varnish足夠輕量級,足夠好用,但是在巨大的併發請求來說,單個varnish所能夠承載的併發訪問量大概在5000個連接請求左右,超出5000個可能就就得不穩定了;而在這裏squid就能表現出良好的性能了,因此在大規模的企業級應用中仍然是以squid居多,而在中小規模的自己公司的反向代理緩存中varnish居多;

3、varnish的日誌說明
   爲了與系統的其它部分進行交互,Child進程使用了可以通過文件系統接口進行訪問的共享內存日誌(shared memory log),因此,如果某線程需要記錄信息,其僅需要持有一個鎖,而後向共享內存中的某內存區域寫入數據,再釋放持有的鎖即可。而爲了減少競爭,每個worker線程都使用了日誌數據緩存。
   共享內存日誌大小一般爲90M,其分爲兩部分,前一部分爲計數器,後半部分爲客戶端請求的數據。varnish提供了多個不同的工具如varnishlog、varnishncsa或varnishstat等來分析共享內存日誌中的信息並能夠以指定的方式進行顯示。

4、VCL基本介紹
   Varnish Configuration Language (VCL)是varnish配置緩存策略的工具,它是一種基於“域”(domain specific)的簡單編程語言,它支持有限的算術運算和邏輯運算操作、允許使用正則表達式進行字符串匹配、允許用戶使用set自定義變量、支持if判斷語句,也有內置的函數和變量等。使用VCL編寫的緩存策略通常保存至.vcl文件中,其需要編譯成二進制的格式後才能由varnish調用。事實上,整個緩存策略就是由幾個特定的子例程如vcl_recv、vcl_fetch等組成,它們分別在不同的位置(或時間)執行,如果沒有事先爲某個位置自定義子例程,varnish將會執行默認的定義。

   VCL策略在啓用前,會由management進程將其轉換爲C代碼,而後再由gcc編譯器將C代碼編譯成二進制程序。編譯完成後,management負責將其連接至varnish實例,即child進程。正是由於編譯工作在child進程之外完成,它避免了裝載錯誤格式VCL的風險。因此,varnish修改配置的開銷非常小,其可以同時保有幾份尚在引用的舊版本配置,也能夠讓新的配置即刻生效。編譯後的舊版本配置通常在varnish重啓時纔會被丟棄,如果需要手動清理,則可以使用varnishadm的vcl.discard命令完成。


5、varnish的後端存儲
   varnish的緩存對象在每次服務重啓時都會被清空並重新建立,所以這些服務器都是不應該隨便去重啓的,varnish爲了把數據更持久化的存儲,引入了更多的存儲機制,所以varnish支持多種不同的後端存儲;

   varnish支持多種不同類型的後端存儲,這可以在varnishd啓動時使用-s選項指定。後端存儲的類型包括:
   (1)file:使用特定的文件存儲全部的緩存數據,並通過操作系統的mmap()系統調用將整個緩存文件映射至內存區域(如果條件允許);
   (2)malloc:使用malloc()庫調用在varnish啓動時向操作系統申請指定大小的內存空間以存儲緩存對象;
   (3)persistent(experimental):與file的功能相同,但可以持久存儲數據(即重啓varnish數據時不會被清除);仍處於測試期;

   varnish無法追蹤某緩存對象是否存入了緩存文件,從而也就無從得知磁盤上的緩存文件是否可用,因此,file存儲方法在varnish停止或重啓時會清除數據。而persistent方法的出現對此有了一個彌補,但persistent仍處於測試階段,例如目前尚無法有效處理要緩存對象總體大小超出緩存空間的情況,所以,其僅適用於有着巨大緩存空間的場景。

   選擇使用合適的存儲方式有助於提升系統性,從經驗的角度來看,建議在內存空間足以存儲所有的緩存對象時使用malloc的方法,反之,file存儲將有着更好的性能的表現。然而,需要注意的是,varnishd實際上使用的空間比使用-s選項指定的緩存空間更大,一般說來,其需要爲每個緩存對象多使用差不多1K左右的存儲空間,這意味着,對於100萬個緩存對象的場景來說,其使用的緩存空間將超出指定大小1G左右。另外,爲了保存數據結構等,varnish自身也會佔去不小的內存空間。

6、varnish的工作原理及工作流程
   官方提供的工作流程圖:

   wKiom1N3Bp6DgfV-AAIqXsglWKg749.jpg
vcl的工作方式是基於狀態引擎(state engine)來實現的;上圖說明:
   vcl_recv的結果如果可以查詢緩存並可以識別,那就要到vcl_hash這步了,如果無法識別那就通過pipe(管道)送給vcl_pipe,如果能識別,但不是一個可緩存的對象,那就通過pass送到vcl_pass去,vcl_hash之後就可查看緩存中有沒有了,有這個請求的對象就表示命中(vcl_hit),如果沒有那就表示未命中(vcl_miss),如果命中的就可以直接通過deliver直接送給vcl_deliver響應了,如果未命中就通過fetch交給vcl_fatch去後端服務器上去取數據,取回數據之後如果數據可以緩存就緩存(cache),本地緩存完之後再構建響應,如果不可以緩存就不做緩存交給vcl_deliver響應了;而如果命中了交給vcl_pass,交給pass之後就要到Fetch objet from backend後端服務器上去取數據了,這是因爲這個命中的對象可能是過期或者是要做單獨立額外的處理的;這就是vcl的狀態引擎過程。


一、安裝實現過程:

# 安裝varnish,版本是3.0.4-1.el6

[root@node0 ~]# rpm -ivh varnish-3.0.4-1.el6.x86_64.rpm varnish-docs-3.0.4-1.el6.x86_64.rpm varnish-libs-3.0.4-1.el6.x86_64.rpm

[root@node0 ~]# rpm -ql varnish  # 查看varnish的安裝文件
[root@node0 ~]# vim /etc/sysconfig/varnish  # 查看配置文件
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_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_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}"    # 使用定義的各高級配置的參數

# 定義後端服務器
[root@node0 sysconfig]# cd /etc/varnish/

[root@node0 varnish]# cp default.vcl default.vcl.bak

[root@node0 varnish]# mv default.vcl test.vcl

[root@node0 varnish]# vim test.vcl
backend webserver {
 .host = "172.16.27.1";  # 後端服務器的地址
 .port = "80";           # 後端服務監聽的端口

}

# 啓動服務

[root@node0 sysconfig]# service varnish start
Starting varnish HTTP accelerator:                         [  OK  ]
[root@node0 sysconfig]#


# 可以進入varnish的命令操作,進去後直接輸入help就可以查看幫助信息;
[root@node0 varnish]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
200 201    
-----------------------------
Varnish Cache CLI 1.0
-----------------------------
Linux,2.6.32-431.el6.x86_64,x86_64,-smalloc,-hcritbit

Type 'help' for command list.
Type 'quit' to close CLI session.

varnish> help
200 377    
help [command]
ping [timestamp]
auth response
quit
banner
status
start
stop
stats
vcl.load <configname> <filename>
vcl.inline <configname> <quoted_VCLstring>
vcl.use <configname>
vcl.discard <configname>
vcl.list
vcl.show <configname>
param.show [-l] [<param>]
param.set <param> <value>
purge.url <regexp>
purge <field> <operator> <arg> [&& <field> <oper> <arg>]...
purge.list

   安裝配置好後端web服務器並啓動,地址爲172.16.27.1,而後通過varnish服務器地址訪問後端服務器,這裏僅僅只是定義一指向後端服務器,也就說現在也只能工作起來,但是還沒有定義相關的緩存屬性等信息,那就先通過varnish服務器端訪問一下先吧:

[root@node1 ~]# yum -y install httpd php php-mysql

[root@node1 ~]# cd /var/www/html

[root@node1 html]# vim index.html

<h1>www.tanxw.com and varnish fo backend</h1>
<h2>node1.tanxw.com</h2>

wKioL1N3LPuirP0DAADyvwScqLY595.jpg


二、設置響應是否命中,接着繼續編寫配置文件:

[root@node0 varnish]# vim test.vcl

sub vcl_deliver {            # 定義子例程

 if (obj.hits > 0){         # 判斷如果命中了就在http響應首部設置X-Cache爲HIT

   set resp.http.X-Cache = "HIT from " server.ip;  

 } else {                   # 否則就在http響應首部設置X-Cache爲MISS

   set resp.http.X-Cache = "MISS";
 }

}

# 在varnish的命令行中重新編譯重新加載配置文件

varnish> vcl.load test1 /etc/varnish/test.vcl

200 13      
VCL compiled.
vcl.use test1
200 0      

varnish> vcl.show test1
200 191


backend webserver {
 .host = "172.16.27.1";
 .port = "80";
}

sub vcl_deliver {
 if (obj.hits > 0){
   set resp.http.X-Cache = "HIT";
 } else {
   set resp.http.X-Cache = "MISS";
 }
}

[root@node0 varnish]# curl -I http://172.16.27.88/index.html  # 也可以在命令行請求

然後再到頁面上訪問看一下是否已經生效:

wKiom1N3LTag8tGIAAPzTTH4mcM097.jpg


三、指定某些文件不能查緩存,斷續添加配置文件

[root@node0 varnish]# vim test.vcl   # 添加如下代碼

sub vcl_recv {     # 定義請求的文件中如果匹配test.html就pass,就不查緩存

 if (req.url ~ "test.html"){
   return(pass);
 }
 return(lookup);

}

# 再到varnish的命令行中重新加載配置文件並應用

varnish> vcl.load test4 /etc/varnish/test.vcl
200 13      
VCL compiled.
vcl.use test4
200 0

# 在命令行請求看一下緩存,不管怎麼請求X-Cache都是MISS

[root@node0 varnish]# curl -I http://172.16.27.88/test.html
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 17 May 2014 09:51:07 GMT
ETag: "120905-1a-4f99578180449"
Accept-Ranges: bytes
Content-Length: 26
Content-Type: text/html; charset=UTF-8
Date: Sat, 17 May 2014 10:01:27 GMT
X-Varnish: 1309371381
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: MISS

而後再請求test.html頁面;

wKioL1N3bEWxPpfMAAIddvy86EY909.jpg


四、設定緩存時長和定義圖片防盜鏈

[root@node0 varnish]# vim default.vcl

sub vcl_fetch {

 if (req.url ~ "\.(jpg|jpeg|gif|png)$") {  # 如果url是以圖片格式結尾的緩存2小時
   set beresp.ttl = 7200s;
 }
 if (req.url ~ "\.(html|css|js)$") {   # 如果url是以html|css|js結尾的緩存20分鐘
   set beresp.ttl = 1200s;
 }

}

sub vcl_recv {
 if (req.url ~ "test.html"){
   return(pass);
 }

# 圖片防盜鏈
 if (req.http.referer ~ "http://.*") {
   if (!(req.http.referer ~ "http://.*tanxw\.com"
      ||req.http.referer ~ "http://.*google\.com"
      ||req.http.referer ~ "http://.*yahoo\.com"
      ||req.http.referer ~ "http://.*google\.cn"
      ||req.http.referer ~ "http://.*baidu\.com"
      )) {
       set req.http.host = "www.tanxw.com";
       set req.url = "/templets/default/p_w_picpaths/logl.gif";
   }
       return (lookup);
 }
 return(lookup);
}


五、移除單個緩存對象:purge用於清理緩存中的某特定對象及其變種(variants),因此,在有着明確要修剪的緩存對象時可以使用此種方式。HTTP協議的PURGE方法可以實現purge功能,不過,其僅能用於vcl_hit和vcl_miss中,它會釋放內存工作並移除指定緩存對象的所有Vary:-變種,並等待下一個針對此內容的客戶端請求到達時刷新此內容。另外,其一般要與return(restart)一起使用。

[root@node0 varnish]# vim default.vcl

acl purgers {    # 定義acl訪問控制,只允許以下網段或主機執行purgers操作

   "127.0.0.1";
   "172.16.0.0"/16;
}

sub vcl_recv {
   if (req.request == "PURGE") {   # 如果請求方法是PURGE,並且客戶端IP在上面定義的網段內的就允許執行PURGE操作,否則生成一個錯誤頁面返回給用戶
       if (!client.ip ~ purgers) {
           error 405 "Method not allowed";
       }
       return (lookup);
   }
}
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") {   # 如在pass中要清除緩存,直接返回錯誤碼
       error 502 "PURGE on a passed object";
   }

}

# 保存退出,而後直接在命令行中進行測試一下:

[root@node0 varnish]# curl -I http://172.16.27.88/index.html
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 17 May 2014 08:07:17 GMT
ETag: "120904-47-4f99404c6fde2"
Content-Type: text/html; charset=UTF-8
Content-Length: 71
Accept-Ranges: bytes
Date: Sat, 17 May 2014 13:38:51 GMT
X-Varnish: 364681188 364681185
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: HIT from 172.16.27.88  # 緩存命中

[root@node0 varnish]# curl -X PURGE http://172.16.27.88/index.html

<?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>200 Purged OK.</title>   # 緩存清除成功
 </head>
 <body>
   <h1>Error 200 Purged OK.</h1>
   <p>Purged OK.</p>
   <h3>Guru Meditation:</h3>
   <p>XID: 364681189</p>
   <hr>
   <p>Varnish cache server</p>
 </body>
</html>

[root@node0 varnish]# curl -I http://172.16.27.88/index.html
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 17 May 2014 08:07:17 GMT
ETag: "120904-47-4f99404c6fde2"
Content-Type: text/html; charset=UTF-8
Content-Length: 71
Accept-Ranges: bytes
Date: Sat, 17 May 2014 13:42:39 GMT
X-Varnish: 364681194
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: MISS   # 清除後緩存MISS了



六、Varnish檢測後端主機的健康狀態
   Varnish可以檢測後端主機的健康狀態,在判定後端主機失效時能自動將其從可用後端主機列表中移除,而一旦其重新變得可用還可以自動將其設定爲可用。爲了避免誤判,Varnish在探測後端主機的健康狀態發生轉變時(比如某次探測時某後端主機突然成爲不可用狀態),通常需要連續執行幾次探測均爲新狀態纔將其標記爲轉換後的狀態。
   每個後端服務器當前探測的健康狀態探測方法通過.probe進行設定,其結果可由req.backend.healthy變量獲取,也可通過varnishlog中的Backend_health查看或varnishadm的debug.health查看。

.probe中的探測指令常用的有:
   (1) .url:探測後端主機健康狀態時請求的URL,默認爲“/”;
   (2) .request: 探測後端主機健康狀態時所請求內容的詳細格式,定義後,它會替換.url指定的探測方式;比如:
   .request =
       "GET /.healthtest.html HTTP/1.1"
       "Host: www.magedu.com"
       "Connection: close";
   (3) .window:設定在判定後端主機健康狀態時基於最近多少次的探測進行,默認是8;
   (4) .threshold:在.window中指定的次數中,至少有多少次是成功的才判定後端主機正健康運行;默認是3;
   (5) .initial:Varnish啓動時對後端主機至少需要多少次的成功探測,默認同.threshold;
   (6) .expected_response:期望後端主機響應的狀態碼,默認爲200;
   (7) .interval:探測請求的發送週期,默認爲5秒;
   (8) .timeout:每次探測請求的過期時長,默認爲2秒;

backend webserver {
   .host =
"www.magedu.com";    # 定義後端主機
   .probe = {

      .url = "/.healthtest.html";  # 向後端主機獲取這個頁面

      .interval = 1s;   # 每隔1秒鐘嘗試一次

      .window = 5;      # 最多嘗試5次,判斷採樣的樣本

      .threshold = 2;   # 如果採樣5次,如果有2次是錯誤的,就認爲是失敗的,上線也是一樣

   }
}


七、Varnish的命令行工具

varnishadm命令語法:varnishadm [-t timeout] [-S secret_file] [-T address:port] [-n name] [command [...]]

通過命令行的方式連接至varnishd進行管理操作的工具,指定要連接的varnish實例的方法有兩種:
-n name —— 連接至名稱爲“name”的實例;
-T address:port —— 連接至指定套接字上的實例;
其運行模式有兩種,當不在命令行中給出要執行的"command"時,其將進入交互式模式;否則,varnishadm將執行指定的"command"並退出。要查看本地啓用的緩存,可使用如下命令進行。
# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 storage.list


總結:

   varnish是一個很強大的緩存服務器,還可以做動靜分離,這裏限於篇幅,在這裏就不一一例舉了,有關信息可以參數官方文檔,還可以做後端服務器的調度等,於是這裏總結得過於倉促,有做得不到之處還望大神多多指出,如果有什麼問題可以留言交流學習。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章