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請求的一個運行流程圖。

0d6b36c4807af2aa5e90bf0df1347236.png

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)^images.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;
}

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

464c9dc6177301679dc868e5a8af19a7.jpg

    以上只是個人見解,有什麼錯誤還望各位大神前來指教哦j_0058.gif

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