varnish緩存服務

緩存的基礎知識
1、程序本身具有局部性
時間局部性

過去訪問到的數據,也有可能被兩次訪問

空間局部性

一個數據被訪問到時,離它最近的文件可能馬上也會被訪問


2、命中率
文檔命中率

從文檔個數進行衡量

字節命中率

從內容大小進行衡量


3、緩存系統的特性
緩存對象

有生命週期,且是定期清理的

緩存空間耗盡

使用LRU(最近最少使用算法)或者MRU算法進行緩存項清理

不可緩存項

用戶私有數據


4、緩存系統一般處理步驟
接收請求
解析請求

提取請求的URL及各種首部

查詢緩存
新鮮度檢測
創建響應報文
發送響應報文
記錄日誌


5、新鮮度檢測機制
過期日期
HTTP/1.0 : expires(其是一個絕對時間)

HTTP/1.1 : Cache-Control: max-age=600(其是一個相對時間)

產效性再驗證(revalidate)

1) 如果原始內容未改變,則僅響應首部信息,響應碼爲304(not modified)
2) 如果原始內容發生了改,則正常響應,響應碼爲200
3)如果原始內容消失,則響應404,此時緩存中的cache object也應該被刪除

條件式請求方式
If-Modified-Since : 基於請求內容的時間戳作驗證
If-unmodified-Since
If-Match

If-None-Match : 結合Etag對文件做MD5校驗


6、HTTP緩存相關首部
age : 一個緩存對象從產生到此刻爲止,經過的多少時間

Cache-Control請求首部
no-cache : 不要從緩存中返回內容

max-age : 相對過期時間,是以秒爲單位

max-stale : 可以接受的對象,但是過期時間必須小於max-stale值

min-fresh : 接受其新鮮生命期大於其當前age跟min-fresh值之各的緩存對象

Cache-Control響應首部
no-cache : 可以緩存,但要跟web服務器再驗證

no-store : 不允許緩存

public : 可以用cache內容迴應任何用戶

private : 只能用緩存內容迴應先前請求該內容的那個用戶

max-age : 本響應包含的對象的過期時間

s-maxage : 公共緩存的最大生命週期

must-revlidate : 每次響應給客戶端時,必須做有效性驗證

緩存時需要考慮到的特殊首部
Authorization:跟授權相關的首部
cookie:用戶識別相關的首部
Vary:accept-encoding:所能接受的字符編碼格式

以上三種首部未特性情況下是不予緩存

通常與緩存相關的方法:
1、GET
2、HEAD


7、常見的緩存服務開源解決方案
varnish : 專用於web服務的緩存

squid : 類似nginx,apache,但比varnish穩定

Varnish
varnish對比squid的優點

1、varnish的穩定性很高,兩者在完成相同負荷的工作時,squid服務器發生故障的機率要高於varnish,因爲squid要經常重啓
2、varnish訪問速度更快,其採用了"Visual Page Cache"技術,所有緩存數據都直接從內存中讀取,而squid是從硬盤讀取,因而varnish在訪問速度方面會更快
3、varnish可以支持更多的併發連接,因爲varnish的TCP連接釋放要比squid快,因而在高併發連接情況下可以支持更多TCP連接
4、varnish可以通過管理端口,使用正則表達式批量的清除部分緩存,而squid是做不到的。
5、squid屬於單進程使用單核CPU,但Varnish是通過fork形式打開多進程來做處理,所以是合理的使用所有核來處理相應的請求

varnish對比squid的缺點

1、varnish進程一旦Hang、Crash或者重啓,緩存數據都會從內存中完全釋放,此時所有請求都會發送到後端服務器,在高併發情況下,會給後端服務器造成很大的壓力
2、在varnish使用中,如果單個vrl的請求通過HA/F5,每次請求不同的varnish服務器時,被請求的varnish服務器都會被穿透到後端,而同樣的請求會在多臺服務器上緩存 ,也會造成varnish的緩存資源浪費,也會造成性能下降

varnish的工作進程特性


varnish工作進程示意圖

2


Management(主進程)
實現應用新的配置
編譯VCL
監控Varnish的子進程(其management每隔幾秒進行子進程探測,如較長時間沒有迴應探測它將重啓一個子進程)
初始化varnish
提供命令行接口

Child/Cache
accept : 接收新的連接請求,交由worker線程處理
worker : 用於處理並響應用戶請求
expiry : 管理過期緩存,從緩存中清理過期的Cache

Vcl compiler
把配置文件編譯成VCL格式

C compiler
C編譯器,vcl compiler調用c compiler

日誌
  shared memory log,共享內存日誌大小默認一般爲90M+,分爲兩部分組成,前一部分爲計數器,後一部分請求響應的相關數據,日誌保存在一個共享的內存空間,只能保存最近最新的日誌,需要使用工具,把日誌不斷的導出,以實現長期保存       


varnishlog : 其以守護進程方式運行,需要將其重啓纔會把日誌導入到本地磁盤,類似於httpd日誌的comm格式

varnishncsa : 其與varnishlog類似,但日誌的格式與httpd的combind格式類似


varnish的進程工作特性

varnish啓動或有2個進程master(management)進程和child(worker)進程,master讀入存儲配置命令,進行初始化,然後fork並監控child,child則分配線程進行cache工作,child還會做管理線程生成很多的worker線程
child線程主線程初始化過程中,將存儲大文件整個加載到內存中,如果該文件超出系統的虛擬內存,則會減少原來配置MMAP大小,然後繼續加載,這時候創建並初始化空間存儲結構體,放在存儲管理的struct中,等待分配
接着varnish某個負責接受http連接的線程開始等待用戶請求,如果有新http連接,但這個線程只負責接收,然後喚醒等待線程池中的work線程,進行請求處理
work線程讀入uri後,將會查找已有的object,命中直接返回,沒有命中則會從後端服務器中取出來,放到緩存中,如果緩存已滿,會根據LRU算法釋放舊的Object,對於釋放緩存,有一個超時線程檢測緩存中所有object的生命週期,如果緩存過期(ttl),則刪除,釋放相應的存儲內存


varnish使用單進程多線程模型,其worker stats類似於一個線程池,所有的資源將整合在一個工作區中,以降低線程在申請或修改內存時,出現的競爭的可能性,當多個線程同時訪問同個資源時,工作區對資源以施加鎖保證用戶的請求在資源爭用時,後來的線程處於等待狀態,以協調線程的工作。
varnish存儲緩存機制
malloc
  基於內存存儲,在內存中存儲各緩存對象,時間久了會產生緩存碎片,如果分配的內存太大,會降低效率。varnish可能會激活大內存空間分配機制(在Centos6以後),這樣也會降低緩存的查詢效率


file
  所有緩存對象緩存在單個文件中,重啓後將會失效,不支持持久機制,建議使用SSD存放緩存數據,一般用於大文件緩存,如圖片等


persistent
  基於文件的持久存儲,varnish重啓了,緩存還有效,其目前爲實驗性項目,生產環境中不能使用


varnish內存分配回收機制


分配
malloc()函數
jemalloc()函數 : 其是malloc的併發實現

回收
free()函數


VCL編程語法
  VCL:varnish configuration language,又被稱之爲DSL(域)編譯語言,其是參照C和perl語言的風格編寫,基本格式如下:


sub NAME {
 ....;



    }

* 不支持循環
* 受狀態引擎的變量,變量的可調用位置與state engine有密切相關性
* 支持終止語句,使用ruturn()返回一個action,其沒有返回值
* 可自定義變量
* //,#,/* */: 用於註釋,會被編譯器忽略
* “域”專用,只能一個域有效
* 操作符: `=,==,~,!,&&,||`
* 條件判斷語句的寫法:

```
單分支:
if (condition) {
    ....;
} else {
    ....;
}

多分支:
if (condition) {
    ...;
} elseif {
    ...;
} else {
    ...;
}
```
* 變量賦值:`set name = value`
* 撤消變量的值:`unset name`
安裝及配置Varnish
Centos 6
安裝
yum install varnish : 默認安裝爲varnish2的版本

主進程配置文件

/etc/sysconfig/varnish

/etc/varnish/default.vcl : VCL引擎配置文件

服務管理腳本
/etc/rc.d/init.d/varnish : varnish服務主進程管理

/etc/rc.d/init.d/varnishlog : varnishlog服務管理

/etc/rc.d/init.d/varnishncsa : varnishncsa服務管理


Centos 7
安裝
yum install varnish : 默認安裝爲varnish 4的版本

主進程配置文件
/etc/varnish/varnish.params : varnish服務主進程管理

/etc/varnish/default.vcl : VCL引擎配置文件

服務管理腳本
/usr/lib/systemd/system/varnish.service
/usr/lib/systemd/system/varnishlog.service
/usr/lib/systemd/system/varnishncsa.service

varnish主配置文件參數說明
RELOAD_VCL=1 : 是否啓動加載VCL配置文件

VARNISH_VCL_CONF=/etc/varnish/default.vcl : varnish的VCL配置文件路徑

VARNISH_LISTEN_PORT=6081 : 默認監聽端口

VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 : 遠程管理監聽地址

VARNISH_ADMIN_LISTEN_PORT=6082 : 遠程管理監聽端口

VARNISH_SECRET_FILE=/etc/varnish/secret : varnish默認加載的密鑰文件,其爲遠程連接varnish的共享密鑰文件

VARNISH_MIN_THREADS=50 : varnish最少啓動worker線程

VARNISH_MAX_THREADS=1000 : varnish最大啓動worker線程數(據說超出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_STORAGE_SHM=64M : 基於內存的存儲緩存,指定緩存的大小,不可與文件的方式共存

VARNISH_STORAGE=“malloc,${VARNISH_STORAGE_SHM}” : varnish基於內存存儲緩存的設置方式

注意:varnish配置文件修改不應重啓服務,而是手動加載配置文件
Varnish管理工具
varnishd命令
其可以在/etc/sysconfig/varnish的配置文件中完成
Options
-f : vcl的配置文件

-a address[:port] :服務監聽的IP地址及端口

-s[name=]type[,options] : varnish緩存的存儲機制
options
malloc[,size]
file[,path[,size[,granularity]]] (力度)
persistent,path,size

-T address[:port] : 指定管理接口,默認6082

-p param=value : 指定參數

-r param : 指定只讀參數2、varnishadm命令
其是通過連接varnish服務端,可以實現varnish服務的管理操作,實現動態加載VCL的配置文件

Options:


-S scret_file : 密鑰文件,跟服務啓動的一樣,在varnish目錄下存放

-T IP:PORT : 連接服務端的端口,默認爲6082


varnish命令行命令


vcl.list : 查看vcl配置文件的列表

vcl.load NAME default.vcl : 加載vcl當前的配置文件爲NAME

vcl.use NAME : 使用加載的配置文件

vcl.show NAME : 查看加載的配置文件詳細配置信息

vcl.discard : 刪除加載的配置文件

backend.list : 查看後端服務器列表及狀態

param.show -l : 查看varnish主進程的運行選項參數

param.set <param> <value> : 在運行時設定主進程的運行參數

ping : 判斷後端服務器的狀態

status : 查看進行的運行狀態

panic.show : 如果某個子進程曾經panic過,可以使用這個命令查看(panic本爲恐慌,代表服務曾經崩潰過)

storage.list: 查看緩存存儲機制

ban  <field> <operator> <arg> [&& <field> <oper> <arg> ]…. : 手動清除緩存

ban.list : 列出清除緩存的規則列表,由上一個參數定義

3、Varnishtop命令
內存日誌區域查看工具
Options
-I REGEXP : 僅顯示被模式匹配到的條目,過濾右邊的信息條目

-X : 僅顯示不被模式匹配到的條目,過濾右邊的信息條目

-i : 過濾左邊字段

-x : 指定的不顯示,不指定的才顯示

-C : 忽略字符大小寫

-d : 顯示已有的日誌
varnishtop -i RxHeader
varnishtop -I ^User-Agent
varnishtop -I ^User-Agent -1 : 只顯示一屏即退出
4、varnishstat命令
varnish的運行統計數據
Options
-f filed,filed,.... : 指明只顯示哪些參數

-l : 列出所有字段

-x : 以XML格式輸出

-j : 以json格式輸出

5、varnishlog命令
啓動以comm格式記錄日誌到磁盤
6、varnishncsa命令
啓動以combind格式記錄日誌到磁盤
VCL Engine
vcl engine是varnish通過VCL配置語言定義的緩存策略,state engine之間有相關性,上級engine通過return指明下級engine,常用的引擎(varnish version 3),如下:
vcl_recv
由return(lookup)定義到vcl_hash引擎


vcl_hash
其是對緩存項進行hash計算


vcl_fetch
其是向後端服務器取文件的


vcl_hit
其下一個工作引擎可能是deliver,也有可能是pass(爲pass的情況下:命中後需要把緩存強制刪除)


vcl_miss
vcl_deliver

vcl_pipe
無法識別的方法,交由pipe管道引擎處理


vcl_pass
可以理解的方法,但不緩存,就經由pass


vcl_error

Vcl engine 完整的工作流程示意圖(1)

2

Vcl engine 完整的工作流程示意圖(2)

2

Vcl engine常見工作流程
1、查詢緩存未命中的工作流

2

2、查詢緩存命中的工作流

2

3、未識別的HTTP方法工作流

2

4、不予緩存的工作流

2

5、完整的工作流

2


restart

當命中後,把url進行了重寫操作,就需要從vcl_recv重新開始,即爲重啓,這類操作被稱之爲restart,但varnish有一個內申機制,即重啓了10次,仍然還在重啓,此次請求將會被丟棄


error

類似於404類的請求錯誤


6、狀態引擎說明

1、首先由vcl_init裝載vcl引擎
2、再由vcl_recv將請求接入,分析是否服務於此請求,並指定如何服務,可能交由下一個(pipe,pass,hash)處理
3、vcl engine可用的return函數

return(lookup) : 送給vcl_hash引擎處理

return(pass) : 交給vcl_pass引擎處理

return(pipe) : 交由vcl_pipe引擎處理

error CODE : 返回錯誤和相應的CODE給客戶端

return(deliver) : 交由vcl_deliver直接投遞
return(hit_for_pass)

return(restart) : 重啓請求


VCl引擎中常用變量


1、在任何引擎中均可使用


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

.host : 後端主機或主機名

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


2、用於處理一個請求階段
可用於recv,hash,pipe,pass引擎中。


client.ip : 客戶端IP地址

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

server.ip : varnish服務器的IP

server.port : varnish服務器的端口

req.request : 客戶端的請求方法

req.url : 客戶端請求的URL

req.proto : http協議版本

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

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

req.http.HEADER : 引用請求報文中指定的首部,哪req.http.host
req.hash_always_miss
req.hash_ignore_busy

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

req.restarts : 此請求被重啓的次數


3、vanish向backend主機發起請求前可用的變量


bereq.request : 請求方法

bereq.url
bereq.proto
bereq.http.HEADER

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


4、當後端服務器響應varnish,但未放置緩存之前


beresp.do_stream : 表示流式響應
流式響應:當後端backend主機響應一個10M大小的文件,10M的文件是由多個數據報文組成,當varnish接收到一個報文時,就直接將報文發給客戶端,也不是等待數據報文接收完整後再發出,這就被稱之爲流式響應


beresp.do_gzip : 從後端服務器收到的響應報文,要不要壓縮以後存儲下來


beresp.do_gunzip : 從後端服務器收到的響應報文,如果壓縮了,要不要解壓縮後再存下來
beresp.http.HEADER
beresp.proto

beresp.status : 響應狀態碼

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

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

beresp.backend.name : 此響應報文來源的backend主機名稱
beresp.backend.ip
beresp.backend.port

beresp.storage : 緩存後端


5、緩存對象存入cache之後可用的變量,大多數爲只讀


obj.response : 服務端所返回的原因短語

obj.proto : 響應時使用的協議
obj.status

obj.ttl : 指明當前的對象緩存的還有多少時長

obj.hits : 這個緩存對象已經命中多少次(大約值)

obj.http.HEADER : 後端服務器的響應首部


6、決定對請求的健做hash計算時可用的變量


req.hash : 把什麼內容做hash鍵,做查詢的健


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


resp.proto : 指定使用什麼協議來響應
resp.status

resp.response
resp.http.HEADER


vcl定義後端服務器主機
定義的後端主機需要在recv中調用,必須會出錯


1、定義格式:
  backend NAME {
      .host=     #後端backend server IP地址
      .port=    #後端backend server 端口
  }


2、後端服務器需要vcl_recv中調用,示例如下
  示例1:

  sub vcl_recv {
      ...
      if(req.url ~ "test.html") {
          set req.backend = NAME;
      } else {
          set req.backend = NAME1;
      }

  示例2:

  sub vcl_recv {
      ...
      set req.backend = NAME;
  }


定義後端服務器集羣
varnish中可以使用director指令將一個或多個近似的後端主機定義成一個邏輯組,並可以指定其調度方法(也叫挑選方法)來輪流將請求發送至後端backend主機上,不同的director可以使用同一個後端主機,而某director也可以使用“匿名”後端主機(在director中直接定義),每個director都必須有其專用名,且在定義後必須在vcl中進行調用,VCL中任何可以指定後端主機的位置均可按需將其替換爲調用某已定義的director


1、定義方法
  backend web1 {
      .host = "www.zhenping.me";
      .port = "80";
  }

  director webservers random {
      .retries = 5;
      {
          .backend = web1;
          .weight = 2;
      }
      {
          .backend = {
              .host = "www2.zhenping.me";
              .port = "80";
          }
      .weight = 3;
      }
  }

  以上爲兩種定義方法示例


2、調用方法
  示例:

  sub vcl_recv {
      ....
      set req.backend = webservers;
  }


3、backend調度方法


round-robin
其是對資源對象的輪詢調度方法(當訪問index1.html到後端server1,當訪問index2.html到後端server2),其沒有參數


random


其是對後端backend server的隨機調度方法,也是建議使用的方法,其支持以下參數


.weight = # : 權重

.retires = # : 來設定查找一個健康後端主機時的嘗試次數

varnish2.1.0之後,random挑選方法又多了兩種變化形式client和hash,client類型的director使用client.identity作爲挑選因子,這意味着client.identity相同的請求都將被髮送至同一後端主機,clinet.itdentity默認爲client.ip,但也可以在VCL中將其修改爲所需要的標識符,類似的,hash類型的director使用hash數據作爲挑選因子,這意味着對同一個URL的請求將被髮往同一個後端主機,其常用於多級緩存的場景中,然後,無論是client還是hash,當其傾向於使用後端主機不可用時將會重新挑選新的後端主機



fallback


用於定義備用服務器,其更多的是冗餘的作用,以下示例
director b3 fallback {
  { .backend = web1; }
  { .backend = web2; }
  { .backend = web3; }
}

注意: 只有web1不可用纔會使用到web2,web2和web1同時不可用時,纔會使用到web3,如果web2不可用,但web1可用,它也會使用web1




varnish檢測後端主機的健康狀態
varnish可以檢測後端主機的健康狀態,在判定後端主機失效時能自動將其從可用後端主機列表中移除,而一旦其重新變得可用還可以自動將其設定爲可用,爲了避免誤判,varnish在探測後端主機的健康狀態發生轉變時(比如某次檢測時某後端主機突然成爲不可用狀態),通常需要連續執行幾次探測均爲新狀態纔將其標記爲轉換後的狀態
每個後端服務器當前探測的健康狀態探測方法通過.probe進行設定,其結果可由req.backend.healthy變量獲取,也可通過varnishlog中的backend_health查看或varnishadm的debug.health查看


.probe中探測常用指令


url : 探測後端主機健康狀態時請求的URL,默認爲/

.request : 探測後端主機健康狀態時所請求內容的詳細格式,定義後,它會替換.rul指定的探測方法,如下:
.request =
  "GET /.healthtest.html HTTP/1.1"
  "Host:www.zhenping.me"
  "Connection: close" #探測時關閉長連接


.window : 設定在判定後端主機健康狀態時基於最近多少次的探測進行,默認是8次


.threshold : 在window中指定的次數中,至少有多少次是成功的才判定後端主機是正常健康運行,默認是3次

.initial : varnish啓動時對後端主機至少需要多少次的成功探測,轉儲同.threshold


.expected_response : 期望後端主機的響應狀態碼,默認是200

interval : 探測請求的發送週期,默認是5秒

.timeout : 每次探測請求的過期時長,默認爲2秒


```
示例1:
    backend web1 {
        .host = "www.zhenping.me";
        .probe = {
            .url = "/.healthtest.html";
            .interval = 1s;
            .window = 5;
            .threshold = 2;
        }
    }

示例2:
    可以將probe的機制定義爲一個代碼塊,在backend中引用

    probe PRO_NAME {
        ....;
    }

    backend NAME {
        ...;
        .probe = PRO_NAME;
    }

```
移除單個緩存對象
purge用於清理緩存中的某特定對象及其變種(variants),因此,在有着明確要修剪的緩存對象時可以使用此種方式。HTTP協議的PURGE方法可以實現purge功能,不過,其僅能用於vcl_hit和vcl_miss中,它會釋放內存工作並移除指定緩存對象的所有Vary:-變種,並等待下一個針對此內容的客戶端請求到達時刷新此內容。另外,其一般要與return(restart)一起使用。下面是個在VCL中配置的示例
acl purgers {
            "127.0.0.1";
            "192.168.0.0"/24;
        }

        sub vcl_recv {
            if (req.request != "GET" &&
                   req.request != "HEAD" &&
                   req.request != "PUT" &&
                   req.request != "POST" &&
                   req.request != "TRACE" &&
                   req.request != "OPTIONS" &&
                   req.request != "DELETE" &&
                   req.request != "PURGE") {    #需要添加PURGE方法,以不被送到PIPE引擎處理
                 /* Non-RFC2616 or CONNECT which is weird. */
                 return (pipe);
               }
               if (req.request != "GET" && req.request != "HEAD" && req.request != "PURGE") { #也需要添加PURGE方法不被送到PASS引擎,以確保PURGE方法可以到達HASH引擎
                 /* We only deal with GET and HEAD by default */
                 return (pass);
             }
            if (req.request == "PURGE") {
                if (!client.ip ~ purgers) {
                    error 405 "Method not allowed";
                }
                return (lookup);
            }
        }
        sub vcl_hit {
            if (req.request == "PURGE") {
                purge;
                error 200 "Purged";
            }
        }
        sub vcl_miss {
            if (req.request == "PURGE") {
                purge;
                error 404 "Not in cache";
            }
        }
        sub vcl_pass {
            if (req.request == "PURGE") {
                error 502 "PURGE on a passed object";
            }
        }

        客戶端在發起HTTP請求時,只需要爲所請求的URL使用PURGE方法即可,其命令使用方式如下:
        # curl -I -X PURGE http://varniship/path/to/someurl
使用示例1
#drop any cookies sent to wordpress
        sub vcl_recv {
            if(!(req.url ~ “wp-(login|admin)”)) {
                unset req.http.cookie;
            }
        }
使用示例2
sub vcl_recv {
            if (req.http.host ~ “(?i)^(www.)?zhenping.me$”) {
                set req.http.host = “www.zhenping.me”;
                set req.backend = www;
            } elseif (req.http.host ~ “(?i)^images.zhenping.me$”) {
                set req.backend = images;
            } else {
                error 404 “Unknown virtual host”;
            }
        }
使用示例3
sub vcl_recv {
    if (req.http.User-Agent ~ "iPad" || req.http.User-Agent ~ "iPhone" || req.http.User-Agent ~ "Android") {
        set req.http.X-Device = "mobile";
    } else {
        set req.http.X-Device = "Desktop";
    }
}
使用示例4(測試是否命中緩存)
 sub vcl_deliver {
        if (obj.hits > 0) {
                set resp.http.X-Cache = "HIT";
        } else {
                set resp.http.X-Cache = "MISS";
        }
     return (deliver);
 }
使用示例5(隱藏後端服務軟件版本)
 sub vcl_deliver {

        if (resp.http.Server) {
                unset resp.http.Server;   #出於安全考慮,需要將後端所使用的軟件名稱和版本隱藏起來
        }
     return (deliver);
 }
生產環境實例
acl purge {
          "localhost";
          "127.0.0.1";
          "10.1.0.0"/16;
          "192.168.0.0"/16;
        }

        sub vcl_hash {
          hash_data(req.url);
          return (hash);
        }

        sub vcl_recv {
          set req.backend = shopweb;
        #  set req.grace = 4h;
          if (req.request == "PURGE") {
            if (!client.ip ~ purge) {
              error 405 "Not allowed.";
            }
            return(lookup);
          }
          if (req.request == "REPURGE") {
            if (!client.ip ~ purge) {
              error 405 "Not allowed.";
            }
            ban("req.http.host == " + req.http.host + " && req.url ~ " + req.url);
            error 200 "Ban OK";
          }
          if (req.restarts == 0) {
            if (req.http.x-forwarded-for) {
              set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.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);
          }
          if (req.request != "GET" && req.request != "HEAD") {
            /* We only deal with GET and HEAD by default */
            return (pass);
          }
          if (req.http.Authorization) {
            /* Not cacheable by default */
            return (pass);
          }


          if ( req.url == "/Heartbeat.html" ) {
            return (pipe);
          }
          if ( req.url == "/" ) {
            return (pipe);
          }
          if ( req.url == "/index.jsp" ) {
            return (pipe);
          }

          if (req.http.Cookie ~ "dper=") {
            return (pass);
          }
          if (req.http.Cookie ~ "sqltrace=") {
            return (pass);
          }
          if (req.http.Cookie ~ "errortrace=") {
            return (pass);
          }
        #   if ( req.request == "GET" && req.url ~ "req.url ~ "^/shop/[0-9]+$" ) {
          if ( req.url ~ "^/shop/[0-9]+$" || req.url ~ "^/shop/[0-9]?.*" ) {
            return (lookup);
          }

         if ( req.url ~ "^/shop/(\d{1,})/editmember" || req.url ~ "^/shop/(\d{1,})/map" || req.url ~ "^/shop/(\d+)/dish-([^/]+)" ) {
            return (lookup);
          }

          return (pass);
        #   return (lookup);
        }

        sub vcl_pipe {
          return (pipe);
        }

        sub vcl_pass {
          return (pass);
        }

        sub vcl_hit {
          if (req.request == "PURGE") {
            purge;
            error 200 "Purged.";
          }
          return (deliver);
        }

        sub vcl_miss {
          if (req.request == "PURGE") {
            error 404 "Not in cache.";
          }
        #   if (object needs ESI processing) {
        #     unset bereq.http.accept-encoding;
        #   }
          return (fetch);
        }


        sub vcl_fetch {
          set beresp.ttl = 3600s;
          set beresp.http.expires = beresp.ttl;
          #set beresp.grace = 4h;
        #   if (object needs ESI processing) {
        #     set beresp.do_esi = true;
        #     set beresp.do_gzip = true;
        #   }

          if ( req.url ~ "^/shop/[0-9]+$" || req.url ~ "^/shop/[0-9]?.*" ) {  
            set beresp.ttl = 4h;
          }

         if ( req.url ~ "^/shop/(\d{1,})/editmember" || req.url ~ "^/shop/(\d{1,})/map" || req.url ~ "^/shop/(\d+)/dish-([^/]+)" ) {
             set beresp.ttl = 24h;
          }

          if (beresp.status != 200){
            return (hit_for_pass);
          }
          return (deliver);
        }

        sub vcl_deliver {
          if (obj.hits > 0){
            set resp.http.X-Cache = "HIT";
          }
          else {
            set resp.http.X-Cache = "MISS";
          }
          set resp.http.X-Powered-By = "Cache on " + server.ip;
          set resp.http.X-Age = resp.http.Age;
          return (deliver);
        }

        sub vcl_error {
          set obj.http.Content-Type = "text/html; charset=utf-8";
          set obj.http.Retry-After = "5";
          synthetic {""} + obj.status + " " + obj.response + {""};
          return (deliver);
        }

        sub vcl_init {
          return (ok);
        }

        sub vcl_fini {
          return (ok);
        }


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