Linux集羣之varnish應用


一、varnish簡介

  varnish是一款高性能且開源的反向代理服務器和HTTP加速器,其採用全新的軟件設置體系機構,和現的的硬件體系緊密配合,與傳遞的squid相比,varnish相比,varnish具有性能更高、速度更快、管理更加方便等諸多優點。

 

二、 varnish軟件體系架構

wKioL1V9cvWxeG4NAAmLiINAung032.bmp


  varnish主要運行兩個進程:Management進程和Child進程。

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


  Child進程包含多種類型的線程,常見的如:

Acceptor線程:接收新的連接請求並響應;

Worker線程:child進程會爲每個會話啓動一個worker線程,因此,在高併發的場景中可能會出現數百個worker線程甚至更多;

Expiry線程:從緩存中清理過期內容;

 

三、 VCL

  VCL是VarnishConfiguration Language,即Varnish配置語言,它提供了一個控制Varnish工作的接口,用於編寫緩存策略配置工具,它是一個基於“域”的簡單編程語言,“域”在一情況下稱爲varnish的狀態引擎。

  下圖來自varnish官方,可以情況的看到每一個狀態的轉換。

wKiom1V9cYXwwbNFAAi3KIVjewo016.bmp

 

3.1 VCL狀態引擎

vcl_recv

請求被接入,但在其被分析、處理完成之前,決定選擇一下步的策略動作

vcl_pipe

不經由varnish直接將請求發往後端主機的時候調用,請求和內容不做任何改變,如同爲客戶端和backend建立一個管道

vcl_pass

將請求直接發給backend,而不是用緩存中的數據響應客戶端

vcl_hash

對URL進行hash,可以自定義hash鍵

vcl_hit

在緩存中找到對象時執行的動作

vcl_miss

未在緩存中找到對象時執行的動作

vcl_fetch

從後端主機獲取內容,並判斷是否緩衝此內容,然後響應給客戶端

vcl_deliver

響應客戶端時執行的動作
vcl_error在varnishi上合成錯誤響應頁時,調用此函數


四、常用變量

4.1 在任何狀態引擎中均可使用

.now:獲取當前系統當前時間

.host:後端主機的IP地址或主機名

.port:後端主機的端口號或服務名


4.2 用於處理請求階段:(vcl_recv,vcl_hash,vcl_pass,vcl_pipe)

client.ip:客戶端的IP地址

server.hostname:varnish緩存服務器的主機名

server.ip:varnish緩存服務器的IP地址

server.port:varnish緩存服務器的端口

req.request:請求方法(例:“GET”,“HEAD”)

req.url: 請求的URL

req.proto: HTTP協議版本

req.backend: 用於服務此次請求的後端主機;

req.backend.healthy: 後端主機健康狀態;

req.http.HEADER: 引用請求報文中指定的首部; (如:req.http.host)

req.can_gzip:客戶端是否能夠接受gzip壓縮格式的響應內容;

req.restarts: 此請求被重啓的次數;(如重定向之後就要進行restart)


4.3 varnish向backend主機發起請求前可用的變量

bereq.request: 請求方法

bereq.url

bereq.proto

bereq.http.HEADER

bereq.connect_timeout: 等待與backend建立連接的超時時長


4.4 backend主機的響應報文到達本主機(varnish)後,將其放置於cache中之前可用的變量

beresp.do_stream: 流式響應;

beresp.do_gzip:是否壓縮之後再存入緩存;默認爲false

beresp.do_gunzip:當收到後端服務器的壓縮報文時,是否解壓之後在存入緩存,默認爲false

beresp.http.HEADER

beresp.proto:

beresp.status:響應狀態碼

beresp.response:響應時的原因短語

beresp.ttl:響應對象剩餘的生存時長,單位爲second;

beresp.backend.name: 此響應報文來源backend名稱;

beresp.backend.ip:此響應報文來源backend IP地址

beresp.backend.port:此響應報文來源backend 端口

beresp.storage


4.5 緩存對象存入cache之後可用的變量

obj.proto:響應時的協議

obj.status:響應時的狀態碼

obj.response:響應時的原因短語

obj.ttl

obj.hits:緩存對象被用於當着響應時的次數

obj.http.HEADER


4.6 在決定對請求鍵做hash計算時可用的變量

req.hash:指明把什麼當着hash鍵來查詢緩存的鍵


4.7 在爲客戶端準備響應報文時可用的變量

resp.proto

resp.status

resp.response

resp.http.HEADER


4.8 快速記憶表

wKioL1V9dDCxzGCUAAspjNkfhuI424.bmp

 

五、安裝varnish

5.1 安裝varnish

varnish-3.0.6-1.el6.x86_64.rpm
varnish-docs-3.0.6-1.el6.x86_64.rpm
varnish-libs-3.0.6-1.el6.x86_64.rpm
rpm -ivh varnish-3.0.6-1.el6.x86_64.rpmvarnish-libs-3.0.6-1.el6.x86_64.rpm varnish-docs-3.0.6-1.el6.x86_64.rpm
#可能會依賴於gcc


5.2 查看相應生成的文件

/etc/rc.d/init.d/varnishlog     #把內存中的日誌讀入到日誌文件
/etc/rc.d/init.d/varnishncsa
/etc/rc.d/init.d/varnish    #服務腳本程序
/etc/sysconfig/varnish     #varnish配置文件
/etc/varnish/default.vcl    #varnish策略文件

 

5.3 /etc/sysconfig/varnish配置文件解釋

[root@node-02 ~]# egrep -v "#|^$"/etc/sysconfig/varnish
NFILES=131072       #所能打開的文件數,會自動調整
MEMLOCK=82000       #內存鎖空間
NPROCS="unlimited"  #單個用戶或進程所能運行的線程數
RELOAD_VCL=1        #是否自動裝載緩存策略文件,當使用腳本啓動時varish會自動的去裝載配置文件
VARNISH_VCL_CONF=/etc/varnish/default.vcl   #默認讀取的緩存策略文件路徑
VARNISH_LISTEN_PORT=6081    #varnish監聽的端口,如http的80端口
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1  #用於CLI的管理的地址
VARNISH_ADMIN_LISTEN_PORT=6082      #用於CLI的管理的端口
VARNISH_SECRET_FILE=/etc/varnish/secret     #密碼文件路徑
VARNISH_MIN_THREADS=50          #啓動的最小線程數
VARNISH_MAX_THREADS=1000        #最多的線程數,即爲最大的併發數,不能超出5000,過了就不維定
VARNISH_THREAD_TIMEOUT=120      #空閒線程空閒超時時間
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin       #varnish緩存文件,即爲緩存類型爲文件
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}"


5.4 解析默認的defaults.vcl文件中的vcl_recv中的內容

sub vcl_recv {
   if (req.restarts == 0) {        #重啓次數爲0,即爲第一次訪問
    if(req.http.x-forwarded-for) { #“req.http.x-forwarded-for”的值爲空或爲NULL,即爲真
        set req.http.X-Forwarded-For =  
        req.http.X-Forwarded-For+ ", " + client.ip;     #當“X-Forwarded-For”首部信息中有值,
#就在其值後加上一個“,”和客戶端的IP地址;用於在後端服務器記錄真實的客戶端的IP地址,
    }else {
        set req.http.X-Forwarded-For = client.ip;
    }
    }
   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);          #不是標準的HTTP請求方法,直接送往後端服務器
    }
   if (req.request != "GET" && req.request !="HEAD") {
       /* We only deal with GET and HEAD by default */
       return (pass);      #不是“GET”和“HEAD”就不查詢緩存,直接送往後端服務器
    }
   if (req.http.Authorization || req.http.Cookie) {
       /* Not cacheable by default */
       return (pass);      #當有認證或cookie時不查詢緩存,直接送往後端服務器
    }
   return (lookup);        #任何一個函數在遇到return時就會返回,以下都不在執行;return (lookup)將將由vcl_hash
}


5.5 解析默認的defaults.vcl中的vcl_hash

 subvcl_hash {
    hash_data(req.url);        #獲取請求的URL
    if (req.http.host) {       #如果請求的host不空
        hash_data(req.http.host);  
    } else {
        hash_data(server.ip);
    }
    return (hash);     #重新回到hash從hash計算
 }


5.6 解析默認的defaults.vcl中的vcl_fetch

sub vcl_fetch {
    if (beresp.ttl <= 0s ||    #beresp後端主機的響應報文收到放到緩存之前
        beresp.http.Set-Cookie ||
        beresp.http.Set-Cookie ||
        beresp.http.Vary == "*") {
                /*
                 * Mark as"Hit-For-Pass" for the next 2 minutes
                 */
                set beresp.ttl = 120 s;
                return (hit_for_pass);
    }
    return (deliver);
 }

 

六、varnish實驗

6.1 實驗拓撲

wKiom1V9cszAjYxWAAYLKEMzKwM736.bmp

 

6.2 實驗規劃

(1)實現動靜分離,Web-01用於解析php,Web-01和Web-02都解析靜態的頁面

(2)緩存靜態內容,只緩存GET和HEAD方法,不緩存帶有cookie的內容,URL中包含靜態資源的,不緩存動態資源的請求

(3)增加一個HTTP首部信息,顯示是否命中

(4)設置清理緩存PURGE

(5)實現後端主機的健康檢查

 

6.3 VCL文件defaluts.vcl

[root@node-02 varnish]# egrep -v"#|^$" default.vcl 
probe healthcheck {         #定義健康檢測方法
  .url = "/index.html";
  .interval = 60s;
  .timeout = 0.3 s;
  .window = 8;
  .threshold = 3;
  .initial = 3;
  .expected_response = 200;
}
backend PHP {       #定義後端服務器
 .host = "192.168.9.181";
 .port = "80";
 .probe = healthcheck;
}
backend web {
 .host = "192.168.9.182";
 .port = "80";
 .probe = healthcheck;
}
director svrs random {      #定義服務器組
  {.backend = PHP; .weight = 1;}
  {.backend = web; .weight = 1;}
}
acl purgers {       #定義purger的訪問控制列表
  "127.0.0.1";
  "192.168.9.0"/24;
}
 subvcl_recv {
    if (req.restarts == 0) {
    if (req.http.x-forwarded-for) {
        setreq.http.X-Forwarded-For =
        req.http.X-Forwarded-For + ", "+ client.ip;
    } else {
        setreq.http.X-Forwarded-For = client.ip;
    }
    }
    #如果是PURGE方法,且不是ACL中的,直接返回錯誤,如果是ACL中的將繼續
    if (req.request == "PURGE") {
        if (!client.ip ~ purgers) {
            error 405 "Method not allowd";
        }
        return(lookup);
    }
    #非指定的HTTP請求訪求,將直接訪問後端服務器
    if (req.request != "GET" &&
      req.request != "HEAD" &&
      req.request != "PUT" &&
      req.request != "POST" &&
      req.request != "TRACE" &&
      req.request != "OPTIONS" &&
      req.request != "PURGE" &&
      req.request !="DELETE") {
        return (pipe);
    }
    #如果不是GET、HEAD方法,就不查緩存
    if (req.request != "GET" && req.request !="HEAD") {
        return (pass);
    }
    #如果是認證和cookie則不查緩存
    if (req.http.Authorization || req.http.Cookie) {
        return (pass);
     }
    #如果訪問php頁面,就不查緩存,將會直接指向後端服務器處理請求
    if (req.url ~ "\.php($|\?)") {
    setreq.backend = PHP;
    return(pass);
    }
    #指定後端響應的服務器組
    set req.backend = svrs;
    return (lookup);
 }
 subvcl_pass {
    #如果PURGE被送至此次將會被pass
    if (req.request == "PURGE") {
    error502 "PURGE on a passed object";
     }
    return (pass);
 }
 subvcl_hit {
    #purge函數就是移除這個obj對應的所有變量緩存
    if (req.request == "PURGE") {
    purge;
    error200 "Purged";
     }
    return (deliver);
 }
 subvcl_miss {
    #直接通過vcl_error返回客戶端
   if (req.request == "PURGE") {
    purge;
    error404 "Not in cache";
    }
    return (fetch);
 }
 sub vcl_fetch{
    #如果響應的ttl小於0秒或者使用了cookie或Very,則不緩存直接返回給客戶端
    if (beresp.ttl <= 0s ||
        beresp.http.Set-Cookie ||
        beresp.http.Vary == "*") {
        set beresp.ttl = 120 s;
        return (hit_for_pass);
    }
    #對於GET方法與請求的不同靜態資源使用不同的緩存時長
    if (req.request == "GET") {    
    if(req.url ~ "\.(css|js|html|htm)") {     
        setberesp.ttl = 10m;     
    }     
    elseif(req.url ~ "\.(gif|jpg|jpeg|png)") {     
        setberesp.ttl = 30m;     
    }     
    elseif(req.url ~ "\.ico") {     
        setberesp.ttl = 30d;     
    }     
}  
    return (deliver);
 }
 subvcl_deliver {
    #返回客戶端之前,添加一個HTTP首部信息
    if (obj.hits>0) {
    setresp.http.X-Cache = "HIT";
    } else {
       set resp.http.X-Cache = "MISS";
    }
    return (deliver);
 }


6.4 修改/etc/sysconfig/varnish文件

VARNISH_LISTEN_PORT=80
VARNISH_MEM_SIZE=32M
VARNISH_STORAGE="malloc,${VARNISH_MEM_SIZE}"


6.5 啓動varnish

[root@node-02 ~]# service varnish start
Starting Varnish Cache:                                    [  OK  ]
[root@node-02 ~]# ss -tanp |grep"80" 
LISTEN    0      128                  :::80                      :::*      users:(("varnishd",4134,8))
LISTEN    0      128                   *:80                       *:*      users:(("varnishd",4134,7))


6.6 Web服務頁面顯示信息

Web服務器的配置域名是www.c.org,需要配置DNS服務器或更改hosts文件。

[root@node-01 html]# cat index.htmlindex.php
<h1>Html Static Web-01</h1>
<h1>Real Server node-01</h1>
[root@node-02 html]# cat index.htmlindex.php
<h1>Html Static Web-02</h1>
<h1>Real Server node-02</h1>


6.7 訪問靜態頁面

第一次訪問時未命令中緩存,因爲緩存中根據就沒有緩存到有內容。

wKiom1V9cynAoikbABIDeI-OP5s737.bmp

當再次刷新瀏覽器是查看響應報文首部信息已經是“HIT”

wKiom1V9c3eS8NRWAA_kWOaKqeI016.bmp

6.8 訪問動態內容

當訪問後綴是php頁面時,varnish不會緩存內容,所以不斷的刷新都會是“MISS”

wKiom1V9c7WAnGHdAA8NWDbNK24585.bmp

 

6.9 purge清理緩存

  當更新了後端的Web服務器的內容時,就需要清理一些緩存信息,讓用戶訪問的頁面內容是最新的,使用curl命令通過-X選項傳遞一個HTTP首部信息。需要在hosts文件中添加一條www.c.org的解析,因爲varnish緩存是根據用戶請求的URL作爲鍵的,當你使用IP地址又是一個新的鍵了,所以是無法清理緩存中的信息。

curl -I -X PURGE http://varniship/path/to/someurl

使用crul訪問www.c.org/index.html頁面,查看請求的響應首部信息

[root@node-02 ~]# curl  -I www.c.org/index.html
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 13 Jun 2015 05:41:09GMT
ETag: "120632-1c-5185fabf370f0"
Content-Type: text/html; charset=UTF-8
Content-Length: 28
Accept-Ranges: bytes
Date: Sun, 14 Jun 2015 12:09:40 GMT
X-Varnish: 1227901809
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: MISS           #第一次請求未命中
 
[root@node-02 ~]# curl  -I www.c.org/index.html
HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 13 Jun 2015 05:41:09GMT
ETag: "120632-1c-5185fabf370f0"
Content-Type: text/html; charset=UTF-8
Content-Length: 28
Accept-Ranges: bytes
Date: Sun, 14 Jun 2015 12:09:43 GMT
X-Varnish: 1227901810 1227901809
Age: 2
Via: 1.1 varnish
Connection: keep-alive
X-Cache: HIT        #再次請求命中


清理緩存index.html

[root@node-02 ~]# curl -I -X PURGE  www.c.org/index.html
HTTP/1.1 200 Purged     #第一次清理成功
Server: Varnish
Content-Type: text/html; charset=utf-8
Retry-After: 5
Content-Length: 380
Accept-Ranges: bytes
Date: Sun, 14 Jun 2015 12:14:04 GMT
X-Varnish: 1227901815
Age: 0
Via: 1.1 varnish
Connection: close
X-Cache: MISS
 
[root@node-02 ~]# curl -I -X PURGE  www.c.org/index.html
HTTP/1.1 404 Not in cache       #第二次清理告知沒有緩存
Server: Varnish
Content-Type: text/html; charset=utf-8
Retry-After: 5
Content-Length: 398
Accept-Ranges: bytes
Date: Sun, 14 Jun 2015 12:14:07 GMT
X-Varnish: 1227901816
Age: 0
Via: 1.1 varnish
Connection: close
X-Cache: MISS


 

小結:

  varnish是一個非常優秀強大的緩存服務器,它已經在大型的生產環境中得到了廣泛的運用,已經經得起考驗,不同的業務要根據不同的業務進行分析,一個好的緩存服務器能命中80%的內容,這樣對後端服務器的訪問的壓力是有多麼的重要是你可以想一想的,現在的互聯網產業是嚴重的依賴於緩存服務器。



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