6.2 VCL

Varnish配置


Varnish 的配置系統與其他軟件不同,一般的配置是使用配置指令,用於打開或關閉某個配置項。而 Varnish 使用 VCL 語言進行配置。

查看 varnish 服務的主配置文件

[root@CentOS74 ~]# cat /etc/varnish/varnish.params | grep ^[^#]
RELOAD_VCL=1   #此值爲1時可以在不重啓varnish的情況下加載vcl文件
VARNISH_VCL_CONF=/etc/varnish/default.vcl   #vcl配置文件路徑
VARNISH_LISTEN_PORT=6081    #主進程監聽端口,默認監聽在0.0.0.0
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1   #命令行管理接口監聽IP
VARNISH_ADMIN_LISTEN_PORT=6082           #命令行管理接口監聽端口
VARNISH_SECRET_FILE=/etc/varnish/secret  #連接管理接口所需的祕鑰文件路徑
VARNISH_STORAGE="malloc,256M"            #使用的緩存方式及所能使用的空間大小
VARNISH_USER=varnish                     #以誰的身份運行
VARNISH_GROUP=varnish                    #以哪個組的身份運行
DAEMON_OPTS="-p thread_pool_min=5 -p thread_pool_max=500 -p thread_pool_timeout=300"  #自定義運行時的選項,使用-p設定,可使用多次
  • vcl語法

語法格式:

    從varnish 4.0版本開始,每一個VCL文件都必須在文件開始處說明版本號VCL4.0
    //, # 單行註釋 /* ... */ 多行註釋
    sub 後跟子例程,例如sub vcl_recv { ...}
    不支持循環,受限於引擎的內建變量
    return() 函數使用關鍵字指定下一條引擎,用於實現狀態引擎轉換
    引擎需要單獨定義

請求與請求之間是無關聯的
請求之間在被處理時是被隔離的
VCL中的子程序既不帶參數,也不返回值
VCL中的子程序只能通過HTTP頭交換數據

vcl 有許多默認配置,其內容無法修改。vcl 中的配置可以分爲 Client Side 與 Backend Side

Client Side 中使用的引擎有:
    vcl_recv, vcl_pass, vcl_hit, vcl_miss, vcl_pipe, vcl_purge, vcl_synth, vcl_deliver

Backend Side 中使用的引擎有:
    vcl_backend_fetch, vcl_backend_response, vcl_backend_error

查看 vcl 默認配置

vcl 4.0;   #開頭聲明使用的是vcl 4.0

#######################################################################
# Client side   #面向客戶端側vcl的配置段

sub vcl_recv {
    if (req.method == "PRI") {  #如果請求方法時PRI
	/* We do not support SPDY or HTTP/2.0 */
	return (synth(405));        #narnish4.0不支持HTTP2.0協議,下一跳synth引擎並指定405響應碼
    }
    if (req.method != "GET" &&  #如果請求方法不是常規方法
      req.method != "HEAD" &&
      req.method != "PUT" &&
      req.method != "POST" &&
      req.method != "TRACE" &&
      req.method != "OPTIONS" &&
      req.method != "DELETE") {
        /* Non-RFC2616 or CONNECT which is weird. */
        return (pipe);           #下一跳到pipe引擎
    }

    if (req.method != "GET" && req.method != "HEAD") {   #如果不是GET或HEAD方法
        /* We only deal with GET and HEAD by default */
        return (pass);                                   #下一跳到pass引擎
    }
    if (req.http.Authorization || req.http.Cookie) {     #如果請求中包含敏感信息
        /* Not cacheable by default */
        return (pass);                                   #下一跳到pass引擎
    }
    return (hash);                                       #符合上面所有if語句,則使用hash引擎
}

通過查看上面的默認 vcl 配置,可以總結出 vcl 的基本語法:

sub subroutine {
    ...
}

if CONDITION {
    ...
} else {
    ...
}

return(), hash_data()
  • 內建函數與關鍵字

函數

    regsub(str, regex, sub)  正則匹配替換
    regsuball(str, regex, sub)  正則匹配全局替換
    ban(boolean expression)  
    hash_data(input)  數據hash計算
    synthetic(str)  字符串處理

關鍵字

    call subroutine  調用子例程
    return(action)  返回值
    new  新鍵對象
    set  設置變量
    unset  撤銷變量

操作符

比較操作符
    == , != , ~ , > , >= , < , <=

邏輯操作符
    && , || , !

  • 變量

內建變量

    req.*  request  表示由客戶端發來的請求報文相關
        req.http.*
            req.http.User-Agent, req.http.Referer, ...
    bereq.*  由varnish發往BE主機的httpd請求相關
        bereq.http.*
    beresp.*  由BE主機響應給varnish的響應報文相關
        beresp.http.*
    resp.*  由varnish響應給client相關;
    obj.*  存儲在緩存空間中的緩存對象的屬性;只讀

常用變量

    bereq.*, req.*  
        bereq.http.HEADERS
        bereq.request, req.request  請求方法
        bereq.url, req.url  請求的url
        bereq.proto  請求的協議版本
        bereq.backend  指明要調用的後端主機
        req.http.Cookie  客戶端的請求報文中Cookie首部的值;
        req.http.User-Agent ~ "chrome"

sub vcl_recv {
    if (req.url ~ "(?i)^/(login|admin)") {
        return(pass);
    }
}

 

    beresp.*, resp.*  
        beresp.http.HEADERS
        beresp.status, resp.status  響應的狀態碼
        reresp.proto, resp.proto  協議版本
        beresp.backend.name  BE主機的主機名
        beresp.ttl  後端主機響應的內容的餘下的可緩存時長

sub vcl_backend_response {
    if (beresp.http.cache-control !~ "s-maxage") {
        if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|css|js)$") {
                unset beresp.http.Set-Cookie;
                set beresp.ttl = 3600s;
        }
    }
}

    obj.*
        obj.hits  此對象從緩存中命中的次數
        obj.ttl  對象的ttl值

    server.*
        server.ip  varnish主機的IP
        server.hostname  varnish主機的Hostname
    client.*
        client.ip  發請求至varnish主機的客戶端IP

sub vcl_recv {
    if (req.restarts == 0) {
        if (req.http.X-Fowarded-For) {
            set req.http.X-Forwarded-For = req.http.X-Forwarded-For + "," + client.ip;
        } else {
            set req.http.X-Forwarded-For = client.ip;
        }
    }
}

網絡拓撲如下

  • 緩存的修剪

Purge

purge 對請求的 url 進行清除

acl Admin {
    "192.168.30.174"/32;   #定義管理主機組
    "192.168.30.74";       
}

sub vcl_recv {
    if (req.method == "PURGE") {  #如果使用PURGE方法請求
        if (!client.ip ~ Admin) {
            return(synth(405,"Permission denied for " + client.ip));  #且對端IP屬於管理機組
        }
	    return(purge);   #則下一跳至purge引擎
    }
}

sub vcl_purge {
    return (synth(200,"Purged.")); 
}

acl 定義訪問控制列表

acl 名稱 {
    "IP地址"[/子網掩碼];
    ...
}

在測試的過程中,發現 varnish 在緩存時,會對整個 URL 進行 hash 計算,也就是說

curl http://192.168.30.75
curl http://192.168.30.75/index.html

訪問的內容雖說一樣,但是在緩存時是兩條不同的記錄,但是

curl http://192.168.30.75
curl 192.168.30.75

卻是同一條記錄

Banning

ban 規則推薦在命令行管理接口中使用,在配置文件中定義使用反而更加死板

在命令行管理接口中使用

    語法:ban <field> <operator> <arg>

ban req.http.host ~ (?i)^192.168.30.74  #清除指定目標主機的緩存
200        

ban req.url ~ (?i)^/index.html          #清除指定URL的緩存
200        

在配置文件中使用

sub vcl_recv {
    if (req.method == "BAN") {
	ban("req.http.host == " + req.http.host + " && req.url == " + req.url);
	return(synth(200, "Ban added"));
    }
}
  • Varnish調度

varnish 作爲一個緩存服務器,同時也是一臺代理服務器。所以同樣業具有向後端主機調度的功能。當後端主機有多臺時,就需要在varnish 中定義後端主機組,並根據使用場景規定調度算法。

Round_Robin

vcl 4.0;
import directors;   #在使用調度功能之前,必須事前聲明使用directors模塊

backend server1 {   #定義單個後端主機
    .host = "192.168.30.174";
    .port = "80";
}

backend server2 {
    .host = "192.168.30.69";
    .port = "80";
}

sub vcl_init {   #在init引擎中新鍵組,並將後端主機加載進指定組中
    new webservers = directors.round_robin();
    webservers.add_backend(server1);
    webservers.add_backend(server2);
}

sub vcl_recv {
    set req.backend_hint = webservers.backend();   #在recv引擎中調用指定的組
}
[root@CentOS75 ~]# for i in {1..10} ;do curl http://192.168.30.75/test$i.html ;done
This is 174 test1
This is 69 test2
This is 174 test3
This is 69 test4
This is 174 test5
This is 69 test6
This is 174 test7
This is 69 test8
This is 174 test9
This is 69 test10

Random

sub vcl_init {   #在init引擎中新鍵組,並將後端主機加載進指定組中
    new webservers = directors.random();  #使用random調度算法
    webservers.add_backend(server1,2);
    webservers.add_backend(server2,1);
}
[root@CentOS75 ~]# for i in {1..10} ;do curl http://192.168.30.75/test$i.html ;done
This is 174 test1
This is 174 test2
This is 174 test3
This is 174 test4
This is 174 test5
This is 174 test6
This is 174 test7
This is 69 test8
This is 174 test9
This is 69 test10

基於cookie的session sticky

sub vcl_init {
    new webservers = directors.hash();  #聲明使用hash調度算法
    webservers.add_backend(server1,1);
    webservers.add_backend(server2,1);  #基於hash的會話綁定也支持定義權重
}

sub vcl_recv {
    set req.backend_hint = webservers.backend(req.http.cookie);  #指定進行hash計算的目標
}
  • 健康狀態檢測

probe HTTP_check {
    .url = "/index.html";  #請求的方法
    .interval = 2s;   #檢測頻率
    .timeout = 1s;   #超時時間
    .expected_response = 200;  #期望的到的返回碼
    .window = 5;   #基於最近的多少次檢查來判斷其健康狀態
    .threshold = 4;  #最少成功次數
}

backend server1 {
    .host = "192.168.30.174";
    .port = "80";
    .probe = HTTP_check;   #調用probe方法
}

backend server2 {
    .host = "192.168.30.69";
    .port = "80";
    .probe = HTTP_check;
}

檢測的方法:

    url:檢測時要請求的URL,默認爲”/"; 

    request:發出的具體請求;

request = 
    "GET /.healthtest.html HTTP/1.1"  #使用的方法與協議
    "Host: www.magedu.com"  #請求的host
    "Connection: close"   #短連接

使用 backend.list 查看後端主機狀態

backend.list
200        
Backend name                   Refs   Admin      Probe
default(192.168.30.174,,80)    4      probe      Healthy (no probe)
server1(192.168.30.174,,80)    4      probe      Healthy 5/5
server2(192.168.30.69,,80)     4      probe      Sick 0/5

    語法:backend.set_health <backend_expression> <state>

    state:
sick:管理down
healthy:管理up
auto:probe auto

varnish> backend.set_health server1 sick
200        

varnish> backend.list
200        
Backend name                   Refs   Admin      Probe
default(192.168.30.174,,80)    4      probe      Healthy (no probe)
server1(192.168.30.174,,80)    4      sick       Healthy 5/5   #設定其狀態爲sick
server2(192.168.30.69,,80)     4      probe      Sick 0/5      #檢測狀態爲sick

後端主機屬性

backend default {
    .host = "192.168.30.174";
    .port = "80";
    .connect_timeout = 0.5s;      #連接超時時間
    .first_byte_timeout = 20s;    #響應超時時間
    .between_bytes_timeout = 5s;  #傳輸間隔超時時間
    .max_connections = 50;        #最大併發連接書

}

 

 

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