varnish應用詳解

緩存能分擔許多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工作機制圖解

image

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緩存,並高度上游服務器的一種組織架構,拓普圖如下.

clipboard

[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

clipboard[1]

此時我們嘗試訪問 varnish 緩存服務器,它就會給我們代理到上游服務器上去了.

我們想查看當前使用的緩存策略,可以使用vcl.show 策略名 來查看.

例如: varnish> vcl.show cl1

如果想丟棄該策略,也很簡單,使用另一個vcl策略後,

varnish> vcl.discard cl1   =>用discard命令就可以清除一個策略了

varnish有許多內置的變量, 不同的變量,有自己特定的使用上下文.

 image

有了以上的認識,我們可以定製更多我們所需要的緩存特性了.比如說,我們想知道這次訪問的緩存是否命中,怎麼辦呢?

由以上的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           

clipboard[2]

第二次訪問時,就命中了.

clipboard[3]

有一些頁面,我們是不希望緩存的,那麼我們又應該怎麼實現呢?

比如說,我們不希望 /test/test.html被緩存, 試試看怎麼實現.

我們可以在接收到用戶請求的開始,就判段要訪問的url,如果滿足條件就直接到上游服務器取數據.

修改配置文件,我們需要在vcl_recv 函數的一開始就判斷url是否包含有相應的路徑.

sub vcl_recv {
    if (req.url ~ "^/test/test.html$") {    =>如果是訪問指定的路徑不走緩存,直接去上游服務器取數據.
        return(pass);
    }
}

這樣設定了之後,訪問相應的路徑怎麼着也緩存不了了.

clipboard[4]

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的狀態碼,並提示緩存清理成功

clipboard[5]

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名稱.這樣纔能有效調用,不然會報錯.
}

clipboard[6]

此時重載vcl配置, 訪問 就可以實現隨機調用上游主機了.

我們還可以在varnish上實現動靜分離

if (req.url ~ ".php$") {    =>如果url中,以.php結尾的,讓其使用特定的backedn -> webapache1

        set req.backend=webapache1;

        return(pass);    =>並且不緩存

    }

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