Varnish 4.0 實戰

簡介 css

Varnish 是一款高性能且開源的反向代理服務器和 HTTP 加速器,其採用全新的軟件體系機構,和如今的硬件體系緊密配合,與傳統的 squid 相比,varnish 具備性能更高、速度更快、管理更加方便等諸多優勢; html

目前最新版本是4.0.0,而3.x的版本也是能夠生產環境下使用的穩定版本,但yum源中的2.x版本過於陳舊,不建議使用; 前端

Varnish與Squid的對比 web

相同點 正則表達式

  • 都是一個反向代理服務器; 後端

  • 都是開源軟件; 緩存

Varnish的優點 安全

  • Varnish的穩定性很高,二者在完成相同負荷的工做時,Squid服務器發生故障的概率要高於Varnish,由於使用Squid要常常重啓; 服務器

  • Varnish訪問速度更快,由於採用了「Visual Page Cache」技術,全部緩存數據都直接從內存讀取,而squid是從硬盤讀取,於是Varnish在訪問速度方面會更快; cookie

  • Varnish能夠支持更多的併發鏈接,由於Varnish的TCP鏈接釋放要比Squid快,於是在高併發鏈接狀況下能夠支持更多TCP鏈接;

  • Varnish能夠經過管理端口,使用正則表達式批量的清除部分緩存,而Squid是作不到的;

  • squid屬因而單進程使用單核CPU,但Varnish是經過fork形式打開多進程來作處理,因此能夠合理的使用全部核來處理相應的請求;

Varnish的劣勢

  • varnish進程一旦Hang、Crash或者重啓,緩存數據都會從內存中徹底釋放,此時全部請求都會發送到後端服務器,在高併發狀況下,會給後端服務器形成很大壓力;

  • 在varnish使用中若是單個url的請求經過HA/F5等負載均衡,則每次請求落在不一樣的varnish服務器中,形成請求都會被穿透到後端;並且一樣的請求在多臺服務器上緩存,也會形成varnish的緩存的資源浪費,形成性能降低;

Varnish劣勢的解決方案

  • 針 對劣勢一:在訪問量很大的狀況下推薦使用varnish的內存緩存方式啓動,並且後面須要跟多臺squid服務器。主要爲了防止前面的varnish服 務、服務器被重啓的狀況下,大量請求穿透varnish,這樣squid能夠就擔當第二層CACHE,並且也彌補了varnish緩存在內存中重啓都會釋 放的問題;

  • 針對劣勢二:能夠在負載均衡上作url哈希,讓單個url請求固定請求到一臺varnish服務器上;

對比Varnish 3.x的主要改進

  • 徹底支持流對象;

  • 可後臺獲取失效的對象,即Client/backend分離;

  • 新的vanishlog查詢語言,容許對請求進行自動分組;

  • 複雜的請求時間戳和字節計數;

  • 安全方面的提高;

涉及VCL語法的改變點

  • vcl配置文件需明確指定版本:即在vcl文件的第一行寫上 vcl 4.0;

  • vcl_fetch函數被vcl_backend_response代替,且req.*再也不適用vcl_backend_response;

  • 後端源服務器組director成爲varnish模塊,需import directors後再在vcl_init子例程中定義;

  • 自定義的子例程(即一個sub)不能以vcl_開頭,調用使用call sub_name;

  • error()函數被synth()替代;

  • return(lookup)被return(hash)替代;

  • 使用beresp.uncacheable建立hit_for_pss對象;

  • 變量req.backend.healty被std.healthy(req.backend)替代;

  • 變量req.backend被req.backend_hint替代;

  • 關鍵字remove被unset替代;

詳見: https://www.varnish-cache.org/docs/4.0/whats-new/index.html#whats-new-index

架構及文件緩存的工做流程

  • Varnish 分爲 master 進程和 child 進程;

  • Master 進程讀入存儲配置文件,調用合適的存儲類型,而後建立 / 讀入相應大小的緩存文件,接着 master 初始化管理該存儲空間的結構體,而後 fork 並監控 child 進程;

  • Child 進程在主線程的初始化的過程當中,將前面打開的存儲文件整個 mmap 到內存中,此時建立並初始化空閒結構體,掛到存儲管理結構體,以待分配;

  • 對外管理接口分爲3種,分別是命令行接口、Telnet接口和Web接口;

  • 同時在運行過程當中修改的配置,能夠由VCL編譯器編譯成C語言,並組織成共享對象(Shared Object)交由Child進程加載使用;

Child 進程分配若干線程進行工做,主要包括一些管理線程和不少 worker 線程,可分爲:

  • Accept線程:接受請求,將請求掛在overflow隊列上;

  • Work線程:有多個,負責從overflow隊列上摘除請求,對請求進行處理,直到完成,而後處理下一個請求;

  • Epoll線程:一個請求處理稱爲一個session,在session週期內,處理完請求後,會交給Epoll處理,監聽是否還有事件發生;

  • Expire線程:對於緩存的object,根據過時時間,組織成二叉堆,該線程週期檢查該堆的根,處理過時的文件,對過時的數據進行刪除或重取操做;

HTTP請求基本處理流程

Varnish 處理 HTTP 請求的過程以下

  1. Receive 狀態(vcl_recv):也就是請求處理的入口狀態,根據 VCL 規則判斷該請求應該 pass(vcl_pass)或是 pipe(vcl_pipe),仍是進入 lookup(本地查詢);

  2. Lookup 狀態:進入該狀態後,會在 hash 表中查找數據,若找到,則進入 hit(vcl_hit)狀態,不然進入 miss(vcl_miss)狀態;

  3. Pass(vcl_pass)狀態:在此狀態下,會直接進入後端請求,即進入 fetch(vcl_fetch)狀態;

  4. Fetch(vcl_fetch)狀態:在 fetch 狀態下,對請求進行後端獲取,發送請求,得到數據,並根據設置進行本地存儲;

  5. Deliver(vcl_deliver)狀態:將獲取到的數據發給客戶端,而後完成本次請求;

注:Varnish4中在vcl_fetch部分略有出入,已獨立爲vcl_backend_fetch和vcl_backend_response2個函數;

內置函數(也叫子例程)

  • vcl_recv:用於接收和處理請求;當請求到達併成功接收後被調用,經過判斷請求的數據來決定如何處理請求;

  • vcl_pipe:此函數在進入pipe模式時被調用,用於將請求直接傳遞至後端主機,並將後端響應原樣返回客戶端;

  • vcl_pass:此函數在進入pass模式時被調用,用於將請求直接傳遞至後端主機,但後端主機的響應並不緩存直接返回客戶端;

  • vcl_hit:在執行 lookup 指令後,在緩存中找到請求的內容後將自動調用該函數;

  • vcl_miss:在執行 lookup 指令後,在緩存中沒有找到請求的內容時自動調用該方法,此函數可用於判斷是否須要從後端服務器獲取內容;

  • vcl_hash:在vcl_recv調用後爲請求建立一個hash值時,調用此函數;此hash值將做爲varnish中搜索緩存對象的key;

  • vcl_purge:pruge操做執行後調用此函數,可用於構建一個響應;

  • vcl_deliver:將在緩存中找到請求的內容發送給客戶端前調用此方法;

  • vcl_backend_fetch:向後端主機發送請求前,調用此函數,可修改發日後端的請求;

  • vcl_backend_response:得到後端主機的響應後,可調用此函數;

  • vcl_backend_error:當從後端主機獲取源文件失敗時,調用此函數;

  • vcl_init:VCL加載時調用此函數,常常用於初始化varnish模塊(VMODs)

  • vcl_fini:當全部請求都離開當前VCL,且當前VCL被棄用時,調用此函數,常常用於清理varnish模塊;

VCL中內置公共變量

變量(也叫object)適用範圍

wKioL1NqVKGwmVZcAAQKey0HSFg359.jpg

注:某些地方略有出入,詳細可參考官方文檔;

變量類型詳解

  • req:The request object,請求到達時可用的變量

  • bereq:The backend request object,向後端主機請求時可用的變量

  • beresp:The backend response object,從後端主機獲取內容時可用的變量

  • resp:The HTTP response object,對客戶端響應時可用的變量

  • obj:存儲在內存中時對象屬性相關的可用的變量

具體變量詳見: https://www.varnish-cache.org/docs/4.0/reference/vcl.html#reference-vcl

優雅模式(Garce mode)

Varnish中的請求合併

當幾個客戶端請求同一個頁面的時候,varnish只發送一個請求到後端服務器,而後讓其餘幾個請求掛起並等待返回結果;得到結果後,其它請求再複製後端的結果發送給客戶端;

但若是同時有數以千計的請求,那麼這個等待隊列將變得龐大,這將致使2類潛在問題:

  • 驚羣問題(thundering herd problem),即忽然釋放大量的線程去複製後端返回的結果,將致使負載急速上升;

  • 沒有用戶喜歡等待;

故爲了解決這類問題,能夠配置varnish在緩存對象因超時失效後再保留一段時間,以給那些等待的請求返回過去的文件內容(stale content),配置案例以下:

sub vcl_recv { if (! req.backend.healthy) {
set req.grace = 5m;
} else {
set req.grace = 15s;
}
} sub vcl_fetch { set beresp.grace = 30m;
} # 以上配置表示varnish將會將失效的緩存對象再多保留30分鐘,此值等於最大的req.grace值便可; # 而根據後端主機的健康情況,varnish可向前端請求分別提供5分鐘內或15秒內的過時內容

安裝配置

# 安裝包下載地址:http://repo.varnish-cache.org/redhat/varnish-4.0/el6/ yum localinstall --nogpgcheck varnish-4.0.0-1.el6.x86_64.rpm varnish-libs-4.0.0-1.el6.x86_64.rpm varnish-docs-4.0.0-1.el6.x86_64.rpm vi /etc/sysconfig/varnish # 編輯配置文件,修改以下項 VARNISH_STORAGE_SIZE=100M # 此值根據自身狀況調整,測試環境可調低此值 VARNISH_STORAGE="malloc,${VARNISH_STORAGE_SIZE}" # Varnish 4中默認使用malloc(即內存)做爲緩存對象存儲方式; service varnish start # 啓動varnish,默認外部請求的監聽端口6081,管理端口6082,後端主機127.0.0.1:80 ===========
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 # 登陸管理命令行 varnish> vcl.list # 列出全部的配置 varnish> vcl.load test1 test.vcl # 加載編譯新配置,test1是配置名,test.vcl是配置文件 varnish> vcl.use test1 # 使用配置,需指定配置名,當前使用的配置以最後一次vcl.use爲準 varnish> vcl.show test1 # 顯示配置內容,需指定配置名

實例解析

# # This is an example VCL file for Varnish. # # It does not do anything by default, delegating control to the # builtin VCL. The builtin VCL is called when there is no explicit # return statement. # # See the VCL chapters in the Users Guide at https://www.varnish-cache.org/docs/ # and http://varnish-cache.org/trac/wiki/VCLExamples for more examples. # Marker to tell the VCL compiler that this VCL has been adapted to the # new 4.0 format. vcl 4.0;
import directors;
probe backend_healthcheck { # 建立健康監測  .url = /health.html;  .window = 5;  .threshold = 2;  .interval = 3s; } backend web1 { # 建立後端主機  .host = "static1.lnmmp.com";  .port = "80";  .probe = backend_healthcheck; } backend web2 {  .host = "static2.lnmmp.com";  .port = "80";  .probe = backend_healthcheck; } backend img1 {  .host = "img1.lnmmp.com";  .port = "80";  .probe = backend_healthcheck; } backend img2 {  .host = "img2.lnmmp.com";  .port = "80";  .probe = backend_healthcheck; } vcl_init { # 建立後端主機組,即directors  new web_cluster = directors.random();  web_cluster.add_backend(web1);  web_cluster.add_backend(web2);  new img_cluster = directors.random();  img_cluster.add_backend(img1);  img_cluster.add_backend(img2); } acl purgers { # 定義可訪問來源IP   "127.0.0.1";   "192.168.0.0"/24;
} sub vcl_recv {  if (req.request == "GET" && req.http.cookie) { # 帶cookie首部的GET請求也緩存   return(hash);
}  if (req.url ~ "test.html") { # test.html文件禁止緩存   return(pass);  }  if (req.request == "PURGE") { # PURGE請求的處理   if (!client.ip ~ purgers) {    return(synth(405,"Method not allowed"));   }   return(hash);  }  if (req.http.X-Forward-For) { # 爲發日後端主機的請求添加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;  }  if (req.http.host ~ "(?i)^(www.)?lnmmp.com$") { # 根據不一樣的訪問域名,分發至不一樣的後端主機組   set req.http.host = "www.lnmmp.com";   set req.backend_hint = web_cluster.backend();   } elsif (req.http.host ~ "(?i)^images.lnmmp.com$") {    set req.backend_hint = img_cluster.backend();   }   return(hash);  } sub vcl_hit { # PURGE請求的處理  if (req.request == "PURGE") {   purge;   return(synth(200,"Purged"));  }
} sub vcl_miss { # PURGE請求的處理  if (req.request == "PURGE") {   purge;   return(synth(404,"Not in cache"));  }
} sub vcl_pass { # PURGE請求的處理  if (req.request == "PURGE") {   return(synth(502,"PURGE on a passed object"));  }
} sub vcl_backend_response { # 自定義緩存文件的緩存時長,即TTL值  if (req.url ~ "\.(jpg|jpeg|gif|png)$") {   set beresp.ttl = 7200s;  }  if (req.url ~ "\.(html|css|js)$") {   set beresp.ttl = 1200s;  }  if (beresp.http.Set-Cookie) { # 定義帶Set-Cookie首部的後端響應不緩存,直接返回給客戶端   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"; }
}
相關文章
相關標籤/搜索