天天看點

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工作區。

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

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的工作原理及工作流程

   官方提供的工作流程圖:

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>

<a href="http://s3.51cto.com/wyfs02/M01/28/52/wKioL1N3LPuirP0DAADyvwScqLY595.jpg" target="_blank"></a>

二、設定響應是否命中,接着繼續編寫配置檔案:

[root@node0 varnish]# vim test.vcl

sub vcl_deliver {            # 定義子例程

 if (obj.hits &gt; 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&gt; vcl.load test1 /etc/varnish/test.vcl

200 13      

VCL compiled.

vcl.use test1

200 0       

varnish&gt; vcl.show test1

200 191

 .host = "172.16.27.1";

 .port = "80";

sub vcl_deliver {

 if (obj.hits &gt; 0){

   set resp.http.X-Cache = "HIT";

 } else {

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

<a href="http://s3.51cto.com/wyfs02/M00/28/51/wKiom1N3LTag8tGIAAPzTTH4mcM097.jpg" target="_blank"></a>

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

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

sub vcl_recv {     # 定義請求的檔案中如果比對test.html就pass,就不查緩存

 if (req.url ~ "test.html"){

   return(pass);

 return(lookup);

# 再到varnish的指令行中重新加載配置檔案并應用

varnish&gt; vcl.load test4 /etc/varnish/test.vcl

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頁面;

<a href="http://s3.51cto.com/wyfs02/M00/28/5A/wKioL1N3bEWxPpfMAAIddvy86EY909.jpg" target="_blank"></a>

四、設定緩存時長和定義圖檔防盜鍊:

[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.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/images/logl.gif";

   }

       return (lookup);

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

acl purgers {    # 定義acl通路控制,隻允許以下網段或主機執行purgers操作

   "127.0.0.1";

   "172.16.0.0"/16;

   if (req.request == "PURGE") {   # 如果請求方法是PURGE,并且用戶端IP在上面定義的網段内的就允許執行PURGE操作,否則生成一個錯誤頁面傳回給使用者

       if (!client.ip ~ purgers) {

           error 405 "Method not allowed";

       }

sub vcl_hit {

   if (req.request == "PURGE") {    # 如果緩存中命中,那麼就清除緩存内容

       purge;

       error 200 "Purged";

sub vcl_miss {

   if (req.request == "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

Last-Modified: Sat, 17 May 2014 08:07:17 GMT

ETag: "120904-47-4f99404c6fde2"

Content-Length: 71

Date: Sat, 17 May 2014 13:38:51 GMT

X-Varnish: 364681188 364681185

X-Cache: HIT from 172.16.27.88  # 緩存命中

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

&lt;?xml version="1.0" encoding="utf-8"?&gt;

&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;

&lt;html&gt;

 &lt;head&gt;

   &lt;title&gt;200 Purged OK.&lt;/title&gt;   # 緩存清除成功

 &lt;/head&gt;

 &lt;body&gt;

   &lt;h1&gt;Error 200 Purged OK.&lt;/h1&gt;

   &lt;p&gt;Purged OK.&lt;/p&gt;

   &lt;h3&gt;Guru Meditation:&lt;/h3&gt;

   &lt;p&gt;XID: 364681189&lt;/p&gt;

   &lt;hr&gt;

   &lt;p&gt;Varnish cache server&lt;/p&gt;

 &lt;/body&gt;

&lt;/html&gt;

Date: Sat, 17 May 2014 13:42:39 GMT

X-Varnish: 364681194

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秒;

   .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是一個很強大的緩存伺服器,還可以做動靜分離,這裡限于篇幅,在這裡就不一一例舉了,有關資訊可以參數官方文檔,還可以做後端伺服器的排程等,于是這裡總結得過于倉促,有做得不到之處還望大神多多指出,如果有什麼問題可以留言交流學習。

本文轉自 wei0164 51CTO部落格,原文連結:http://blog.51cto.com/tanxw/1412984,如需轉載請自行聯系原作者

繼續閱讀