天天看點

HTTP加速器——VARNISH

一、前言

   首先來簡單認識一下什麼是varnish?它能實作什麼樣的功能?.......

   Varnish是一款高性能的開源HTTP加速器,常見的緩存服務開源解決方案有varnish、nginx、squid 、ats等,Varnish 的作者Poul-Henning Kamp是FreeBSD的核心開

發者之一,varnish項目是2006年釋出的第一個版本0.9.距今已經有十年了,此文檔之前也提過varnish還不穩定,那是2007年時候編寫的,經過varnish開發團隊和網友們的辛苦耕耘,現在的varnish已經很健壯。很多門戶網站已經部署了varnish,并且反應都很好,甚至反應比squid還穩定,

且效率更高,資源占用更少。挪威最大的線上報紙 Verdens Gang 使用3台Varnish代替了原來的12台Squid,性能比以前更好。相信在反向代理,web加速方

面,varnish已經有足夠能力代替squid。

二、varnish有哪些特性

   1、Varnish的穩定性很高,兩者在完成相同負荷的工作時,Squid伺服器發生故障的幾率要高于Varnish,因為使用Squid要經常重新開機; 

   2、Varnish通路速度更快,因為采用了“Page Cache”技術,所有緩存資料都直接從記憶體讀取(映射),而squid是從硬碟讀取,因而Varnish在通路速度方面會更快; 

   3、Varnish可以支援更多的并發連接配接,因為Varnish的TCP連接配接釋放要比Squid快,因而在高并發連接配接情況下可以支援更多TCP連接配接; 

   4、Varnish可以通過管理端口,使用正規表達式批量的清除部分緩存,而Squid是做不到的; 

   5、squid屬于是單程序使用單核CPU,但Varnish是通過fork形式打開多程序來做處理,

是以可以合理的使用所有核來處理相應的請求。

三、Varnish的安裝方式

   Varnish的安裝非常簡單,常用的兩種方式有yum安裝和源碼包安裝,下面一源碼安裝來簡單介紹:

   1、安裝前的準備

   擷取varnish軟體

   Varnish的官方站點為http://varnish-cache.org,這裡面有varnish的最新說明文檔,以及版本更新記錄,從此站點可以找到varnish在SourceForge的下載下傳連結,目前,varnish的最新版本是Varnish 2.1.2,下載下傳完成後的包名為varnish-2.1.2.tar.gz,此處我們就以此版本為例,進行安裝配置。

   2、建立varnish使用者以及使用者組,并且建立Varnish緩存目錄和日志目錄:

[root@varnish-server ~]#useradd  -s /sbin/nologin varnish

[root@varnish-server ~]#mkdir /data/varnish/cache

[root@varnish-server ~]#mkdir /data/varnish/log

[root@varnish-server ~]#chown -R varnish:varnish  /data/varnish/cache

[root@varnish-server ~]#chown -R varnish:varnish  /data/varnish/log

   3、安裝varnish(在此之前最好安裝yum包組和pcre-devel、openssl-devel,否則編譯時易出錯)

   這裡我們将varnish安裝編譯到/usr/local/目錄下,操作如下:

[root@varnish-server ~]#tar -zxvf varnish-2.1.2.tar.gz

[root@varnish-server ~]#cd varnish-2.1.2

[root@varnish-server ~]#./configure --prefix=/usr/local/varnish \

 >--enable-dependency-trackin 

>--enable-debugging-symbols 

>--enable-developer-warnings 

[root@varnish-server ~]#make

[root@varnish-server ~]#make install

   至此,varnish安裝完畢。

四、配置Varnish

   VCL使用說明

   VCL,即為Varnish Configuation Language,用來定義varnish的存取政策,VCL文法比較簡單,跟C和perl比較相似,可以使用指定運算符“=”,比較運算符“==”,邏輯運算符“!,&&,!!”等形式。還支援正則表達樣和用“~”進行ACL比對運算,同時還可以使用“set”這樣的關鍵字來指定變量。

需要注意的是,“\”字元在VCL裡沒有特别的含義,這點與其它語言略有不同,另外,VCL隻是配置,并不是真正的程式設計語言,沒有循環,也沒有自定義變量。 

   在講述Varnish配置之前,首先需要了解下varnish的配置文法,即VCL,下面對VCL常用的一些内置函數和公用變量進行詳細介紹。

五、VCL狀态

   1、VCL有多個狀态引擎,狀态之間存在相關性,但狀态引擎彼此間互相隔離;每個狀

态引擎可使用return(x)指明關聯至哪個下一級引擎;每個狀态引擎對應于vcl檔案中的一個配置段   

   vcl_recv:接受使用者請求進varnish的入口的引擎,接受到結果之後,利用

return(lookup),将請求轉交給vcl_hash引擎進行處理 

   vcl_hash:接受到使用者請求後,對使用者請求的URL進行hash計算,根據請求的首

部資訊,以及hash結果進行下一步處理的引擎 

   vcl_hit:經過vcl_hash引擎處理後,發現使用者請求的資源本地有緩存,則

vcl_hash引擎通過return(hit)将請求交給vcl_hit引擎進行處理,vcl_hit引擎處理後

将請求交給vcl_deliver引擎,vcl_deliver引擎建構響應封包,響應給使用者 

   vcl_miss:經過vcl_hash引擎處理後,發現使用者請求的資源本地沒有緩存,則

vcl_hash引擎通過return(miss)将請求交給vcl_miss引擎進行處理

   vcl_purge:經過vcl_hash引擎處理後,發現請求是對緩存的内容進行修剪時,則通過

return(purge)交給vcl_purge引擎進行處理,vcl_purge引擎處理後,利用vcl_synth引擎将處

理的結果告知給使用者 

   vcl_pipe:經過vcl_hash引擎處理後,發現使用者請求的封包varnish無法了解,則通過

return(pipe),将請求交給vcl_pipe引擎,pipe引擎直接将請求交給後端真實伺服器 

   vcl_pass:當請求經過vcl_hash處理後,發現請求封包不讓從緩存中進行響應或其他原因沒辦

法查詢緩存,則由return(pass)或return(hit-for-pass)交由vcl_pass引擎進行處理 

   vcl_backend_fetch:當發現緩存未命中或由vcl_pass傳遞過來的某些不能查詢緩存的請求,

交由vcl_backend_fetch引擎處理,vcl_backend_fetch引擎會向後端真實web伺服器發送請

求封包,請求對應的資源 

   vcl_backend_response:當後端發送響應封包到varnish後,會由vcl_backend_resonse引

擎進行處理,如:判斷響應的内容是否可緩存,如果能緩存,則緩存下來後,交給vcl_deliver

引擎,如果不能緩存,則直接交給vcl_deliver引擎,vcl_deliver引擎建構響應封包給用戶端

    varnish4.0版本的兩個特殊的引擎 

    vcl_init:在處理任何請求之前要執行的vcl的代碼,主要用于初始化VMOD,可

用在後端主機有多台時,借助此引擎完成多台主機的負載均衡效果 

    vcl_fini:所有的請求都已經結束,在vcl配置被丢棄時調用;主要用于清理VMOD

   2、VCL處理流程圖

   通過上面對VCL的介紹,讀者對各個函數實作的功能已經有了一個了解,其實每個函數之間都是互相關聯的,下圖列出了varnish處理HTTP請求的一個運作流程圖。

HTTP加速器——VARNISH

VCL處理流程圖

   處理過程大緻分為如下幾個步驟:

(1)Receive狀态,也就是請求處理的入口狀态,根據VCL規則判斷該請求應該是Pass或Pipe,或者進入Lookup(本地查詢)。

(2)Lookup狀态,進入此狀态後,會在hash表中查找資料,若找到,則進入Hit狀态,否則進入miss狀态。

(3)Pass狀态,在此狀态下,會進入後端請求,即進入fetch狀态。 

(4)Fetch狀态,在Fetch狀态下,對請求進行後端的擷取,發送請求,獲得資料,并進行本地的存儲。

(5)Deliver狀态, 将擷取到的資料發送給用戶端,然後完成本次請求。

   3、内置公用變量

   VCL内置的公用變量可以用在不同的VCL函數中,根據這些公用變量使用的不同階段,下面依次介紹。

當請求到達後,可以使用的公用變量如表1所示:

表1

公用變量名稱            含義

req.backend        指定對應的後端主機

server.ip              表示伺服器端IP

client.ip               表示用戶端IP

req.request          指定請求的類型,例如GET、HEAD、POST等

req.url                 指定請求的位址

req.proto            表示用戶端發起請求的HTTP協定版本

req.http.header   表示對應請求中的http頭部資訊

req. restarts         表示請求重新開機的次數,預設最大值為4

Varnish               在向後端主機請求時,可以使用的公用變量如表2所示:

表2

公用變量名稱 含義

beresp.request 指定請求的類型,例如GET、HEAD等

beresp.url 指定請求的位址

beresp .proto 表示用戶端發起請求的HTTP協定版本

beresp .http.header 表示對應請求中的http頭部資訊

beresp .ttl 表示緩存的生存周期,也就是cache保留多長時間,機關是秒

從cache或者後端主機擷取内容後,可以使用的公用變量如表3所示:

表3

obj.status 表示傳回内容的請求狀态代碼,例如200、302、504等

obj.cacheable 表示傳回的内容是否可以緩存,也就是說,如果HTTP傳回是200、203、300、301、302、404、410等,并且有非0的生存期,則可以緩存

obj.valid 表示是否是有效的HTTP應答

obj.response 表示傳回内容的請求狀态資訊

obj.proto 表示傳回内容的HTTP協定版本

obj.ttl 表示傳回内容的生存周期,也就是緩存時間,機關是秒

obj.lastuse 表示傳回上一次請求到現在的間隔時間,機關是秒

對用戶端應答時,可以使用的公用變量如表4所示:

表4

resp.status 表示傳回給用戶端的HTTP狀态代碼

resp.proto 表示傳回給用戶端的HTTP協定版本

resp.http.header 表示傳回給用戶端的HTTP頭部資訊

resp.response 表示傳回給用戶端的HTTP狀态資訊

在上面的講述中,我們隻是介紹了常用的VCL内置公用變量,如果需要了解和使用更多的公用變量資訊,請登入varnish官方網站查閱。

六、Varnish 的 Storage 方式可分為兩種:

  (1)Malloc 通過 malloc 擷取記憶體;

  (2)Mmap file 建立大檔案,通過二分法分段映射成 1G 以内的大塊。

   以 Mmap file 的緩存方式啟動 I/O 也會形成瓶頸,原因主要是 Varnish 緩存的資料先會刷到磁盤上,然後在一次性讀到記憶體中,這在通路量大的時候同時也會對 I/O 造成很大的壓力。Malloc 緩存方式雖然對 I/O 沒有壓力,因所有緩存資料都寫到記憶體中。

七、配置一個簡單的Varnish執行個體

   由于版本的不同,Varnish配置檔案的寫法也存在一定差異,這裡講述的版本是基于centos 7.3版本的varnish4.0,但是該配置檔案一定要以非注釋行vcl 4.0開始,否則服務将不能識别。

   Varnish安裝完成後,預設的配置檔案為/usr/local/varnish/etc/varnish/default.vcl,配置完成的default.vcl檔案如下:

vcl 4.0;
##############啟用負載均衡子產品###############
import directors;
##############配置健康狀态探測##############
probe backend_healthcheck {
    .url = "/index.html";
    .window = 5;      #視窗
    .threshold = 2;   #門檻
    .interval = 3s;
    .timeout  = 1s;
}

#############添加後端主機################
backend web1 { 
    .host = "172.17.254.17";
    .port = "80";
    .probe = backend_healthcheck;
}

backend img1 {
    .host = "172.17.254.107";
    .port = "80";
    .probe = backend_healthcheck;
}


#############定義負載均衡及算法###############
sub vcl_init {
    new web_cluster = directors.random();
    web_cluster.add_backend(web1,10);
    new img_cluster = directors.round_robin();
    img_cluster.add_backend(img1);
}
################定義Purge-ACL控制#######################
acl purgers {    # 定義可通路來源IP
        "127.0.0.1";
        "172.17.0.0"/16;
}

################定義vcl_recv函數段######################
sub vcl_recv {
    if (req.method == "GET" && req.http.cookie) {  
        return(hash);
}
#####如果請求不是GET或者HEAD,不緩存######
if(req.method!="GET"&&req.method!="HEAD"){
return(pass);
}
#####不正常的請求不緩存#####
   if (req.method != "GET" &&
        req.method != "HEAD" &&
        req.method != "PUT" &&
        req.method != "POST" &&
        req.method != "TRACE" &&
        req.method != "OPTIONS" &&
        req.method != "PURGE" &&
        req.method != "DELETE") {
         return (pipe);
   }
   
    if (req.url ~ "index.php") {   
        return(pass);
    }
    if (req.method == "PURGE") {    # PURGE請求的處理
        if (client.ip ~ purgers) {
          #  return(synth(405,"Method not allowed"));
          return(purge);
        }
    }
##為發往後端主機的請求添加X-Forward-For首部##
    if (req.http.X-Forward-For) {    
        set req.http.X-Forward-For = req.http.X-Forward-For + "," + client.ip;
    } else {
        set req.http.X-Forward-For = client.ip;
    }
#####實作動靜分離#####
set req.http.X-Forward-For=client.ip;
        if(req.url~"(?i)\.(jpeg|jpg|png|jif)($|\?)"){
                set req.backend_hint=img_cluster.backend();
}   


####################定義vcl_hash函數段#################
    if (req.http.host ~ "(?i)^(www.)?aaa.com$") {    # 根據不同的通路域名,分發至不同的後端主機組
        set req.backend_hint = web_cluster.backend();
      } elsif (req.http.host ~ "(?i)^p_w_picpaths.aaa.com$") {
            set req.backend_hint = img_cluster.backend();
      }
        return(hash);
}


sub vcl_hash {
     hash_data(req.url);
}

##############設定資源緩存時長#################
sub vcl_backend_response { # 自定義緩存檔案的緩存時長,即TTL值
    if (bereq.url ~ "\.(jpg|jpeg|gif|png)$") {
        set beresp.ttl = 10s;
    }
    if (bereq.url ~ "\.(html|css|js)$") {
        set beresp.ttl = 1200s;
    }
#    if (beresp.http.Set-Cookie) { # 定義帶Set-Cookie首部的後端響應不緩存,直接傳回給用戶端
     set beresp.grace = 30m;
        return(deliver);
#    }
}
sub vcl_deliver {
    if (obj.hits > 0) {    # 為響應添加X-Cache首部,顯示緩存是否命中
        set resp.http.X-Cache = "HIT from " + server.ip;
    } else {
        set resp.http.X-Cache = "MISS";
    }
        unset resp.http.X-Powered-By;
        unset resp.http.Via;
}      

在經過以上的配置之後會出現一下測試界面:

繼續閱讀