原網頁地址:http://me2xp.blog.51cto.com/6716920/1558686php
1. Varnish簡介css
Vanish是一款開源的、高效的的HTTP加速器,能夠提供代理服務和緩存服務。html
目前最新的版本是4.0.X,而生產環境中用的最多的仍是3.x.x的版本。epel源中的rpm過於老舊,不推薦使用。nginx
1.1 Varnish架構數據庫
Varnish也採用了多進程的架構,有2個主要的進程:編程
主進程,也稱管理進程vim
管理進程後端
應用VCL配置,編譯VCL瀏覽器
監視Varnish緩存
初始化Varnish
提供命令行接口
子進程的管理
子進程
管理線程池
移除過時緩存數據
上游服務器通信和健康檢查
將緩存文件映射至內存空間
這些功能須要依靠子進程中衆多不一樣類型的線程完成:
Acceptor線程負責接收新的鏈接和受權給它們
Worker線程,有不少Worker線程,一個會話由一個線程負責處理。
Expiry線程 從緩存中清除過時內容
1.2 VCL
Varnish Configuration Language,縮寫爲VCL,即Varnish配置語言,它提供了一種控制Varnish工做的接口,使用該語言配置緩存的處理策略,而後經過管理進程將其解釋爲C代碼,而後使用C編譯器編譯後,連接至正在運行的Varnish實例。
VCL的語法借鑑了C和Perl的特色。
1.2.1 VCL狀態引擎
VCL一個重要的概念就是狀態。
每一個請求都被單獨處理,處理過程當中將處在不一樣的狀態。退出一種狀態就會轉入下一個狀態。狀態之間是有關聯的,而不是孤立的。
上圖來自官方文檔
能夠清楚的看出狀態的轉換,以及不一樣狀態所要通過的處理函數。
1.2.2 VCL函數
vcl_recv | 用戶請求成功接收後,遇到的第一個函數,能夠在這裏對請求的數據進行處理,並決定選取下一步的處理策略 |
vcl_fetch | 從後端主機獲取內容,並判斷是否緩衝此內容,而後發送給客戶端 |
vcl_hash | 對URL進行hash,能夠自定義hash鍵 |
vcl_pass | 將請求直接發給backend,而不是用緩存中的數據 |
vcl_hit | 在緩存中找到緩存對象時,要執行的操做 |
vcl_miss | 在緩存中未找到對象時,要執行的操做 |
vcl_deliver | 響應給客戶端時調用此方法 |
vcl_pipe | 不經由varnish直接將請求發日後端主機的時候調用,請求和內容不作任何改變,如同爲客戶端和backend創建一個管道 |
vcl_error | 在varnishi上合成錯誤響應頁時,調用此函數 |
這些函數相似或就是回調函數,是Vanish調用用戶邏輯的接口。
1.2.3 內置變量
請求到達時可使用的變量
req.url
req.http
req.http.header
req.restart
server.ip
server.hostname
server.port
req.backend
請求發日後端主機時可使用的變量
bereq.url
bereq.http
bereq.http.header
bereq.proto
bereq.connect_timeout
緩存對象進入cache後可用的變量
obj.response
obj.status
obj.http.header
obj.proto
obj.ttl
obj.hits
後端主機響應的內容可用的變量
beresp.response
beresp.http.header
beresp.ttl
beresp.proto
beresp.do_gzip
beresp.do_gunzip
beresp.backend.name
beresp.backend.ip
響應客戶端可使用的變量
resp.response
resp.proto
resp.status
resp.http.header
上圖能夠看出不一樣變量的使用範圍,其實從變量的類型結合請求處理的流程圖也基本上能夠理解這些變量究竟適合在什麼函數中使用。
還有更多變量請參考官方文檔。
2. Varnish緩存實驗
2.1 規劃
2.2 實驗目標
實現動靜分離
緩存靜態內容
只緩存GET和HEAD方法
不緩存帶有cookie的內容
URL中包含靜態資源的,例如jpg、png、jpeg、gif、html、htm、ico、css、js,就緩存600s
不緩存動態資源請求,例如php
判讀客戶端瀏覽器類型,以返回不一樣的頁面。包括移動終端。
增長一個header標籤,顯示是否命中
設置清理緩存PURGE
實現後端主機的健康檢查
2.3 環境搭建
2.3.1 安裝Varnish
epel源中Varnish版本過低,建議使用官方提供的版本,本文使用3.0.5的三個rpm包安裝,分別是varnish-docs-3.0.5-1.el6.x86_64.rpm、varnish-3.0.5-1.el6.x86_64.rpm和varnish-libs-3.0.5-1.el6.x86_64.rpm。
# yum -y install varnish-* # rpm -ql varnish /etc/logrotate.d/varnish /etc/rc.d/init.d/varnish /etc/rc.d/init.d/varnishlog /etc/rc.d/init.d/varnishncsa /etc/sysconfig/varnish /etc/varnish /etc/varnish/default.vcl /usr/bin/varnish_reload_vcl /usr/bin/varnishadm /usr/bin/varnishhist /usr/bin/varnishlog /usr/bin/varnishncsa /usr/bin/varnishreplay /usr/bin/varnishsizes /usr/bin/varnishstat /usr/bin/varnishtest /usr/bin/varnishtop
2.3.2 WEB1
1) 安裝、啓動php-fpm
# yum install php-fpm -y # service php-fpm start Starting php-fpm: [ OK ]
2) nginx
源碼或者epel源的yum安裝nginx,請參看其餘博文。很簡單
# vim /usr/local/nginx/conf/nginx.conf user nginx; worker_processes auto; #pid /var/run/nginx.pid; error_log /var/log/nginx/error.log; events { use epoll; worker_connections 1024; } http { include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 5; #gzip on; server { listen 80; server_name WEB1; add_header X-upS WEB1-$server_addr:$server_port; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ \.php$ { root html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param QUERY_STRING $query_string; include fastcgi_params; } # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } } # service nginx start
3)提供php測試頁
# vim /usr/local/nginx/html/index.php <h1>This is a PHP Test Page</h1> <?php phpinfo(); ?>
注意這裏response header,後面的測試能夠和這一次進行對比。
4)提供首頁和測試頁
# cat /usr/local/nginx/html/index.html <html> <head> <title>dynamic</title> </head> <body> <h1 align="left">This is a static page of WEB1</h1> </body> </html> # cat /usr/local/nginx/html/test.html Just test for WEB1
2.3.3 WEB2
1) 安裝nginx,提供WEB服務
配置以下:
# vim /usr/local/nginx/conf/nginx.conf user nginx; worker_processes auto; #pid /var/run/nginx.pid; error_log /var/log/nginx/error.log; events { use epoll; worker_connections 1024; } http { include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 5; #gzip on; server { listen 8080; server_name WEB2; add_header X-upS WEB2-$server_addr:$server_port; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
2)提供主頁和測試頁
# cat /usr/local/nginx/html/index.html <html> <head> <title>static</title> </head> <body> <h1 align="center">OK! This is a static page</h1> </body> </html> # cat /usr/local/nginx/html/test.html Just test for WEB2
2.3.4 Varnish主機
1)服務配置文件/etc/sysconfig/varnish,注意如下幾個參數的值
VARNISH_LISTEN_PORT=80
VARNISH_STORAGE_SIZE=100M
VARNISH_VCL_CONF=/etc/varnish/default.vcl
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
VARNISH_ADMIN_LISTEN_PORT=6082
2)VCL文件/etc/varnish/test.vcl,附詳細說明
# 定義檢測方法 probe healthcheck { .url = "/test.html"; .interval = 5s; .timeout = 2 s; .window = 8; .threshold = 3; .initial = 3; .expected_response = 200; } # 定義後端服務器 backend WEB1 { .host = "192.168.23.80"; .port = "80"; .probe = healthcheck; } backend WEB2 { .host = "192.168.23.81"; .port = "8080"; .probe = healthcheck; } # 定義服務器組 director staticserver random { {.backend = WEB1; .weight = 3;} {.backend = WEB2; .weight = 6;} } # purge的訪問控制列表 acl purge { "localhost"; "127.0.0.1"; "192.168.23.0"/24; } sub vcl_recv { 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" && req.request != "PURGE") { /* Non-RFC2616 or CONNECT which is weird. */ return (pipe); } # 若是是PURGE方法,且不是acl中的,直接返回錯誤,若是是acl中的則繼續 if (req.request == "PURGE") { if (! client.ip ~ purge) { error 405 "Not Allowed!"; } return (lookup); } # 若是不是GET和HEAD方法,就不查緩存 if (req.request != "GET" && req.request != "HEAD") { /* We only deal with GET and HEAD by default */ return (pass); } # 若是是認證和Cookie,就不查緩存 if (req.http.Authorization || req.http.Cookie) { /* Not cacheable by default */ return (pass); } # 若是是訪問php頁面,就不查緩存,且直接指向後端的動態網頁處理服務器 if (req.url ~ "\.php($|\?)") { set req.backend = WEB1; return (pass); } # 指定後端響應的服務器,這裏使用了服務器組 set req.backend = staticserver; return (lookup); } sub vcl_hit { # 這裏隱含一個obj,其url就是當前的請求 # purge函數就是移除這個obj對應的全部變量 if (req.request == "PURGE") { purge; error 200 "Purged in hit."; } } sub vcl_miss { # 須要error,直接經過vcl_error返回客戶端 # 這個客戶端是實現清除緩存功能的客戶端,一般來自內部主機,且acl中有了限制 if (req.request == "PURGE") { # 這個地方能夠不用purge # purge; error 200 "Purged in miss."; } } sub vcl_fetch { # 若是響應的ttl小於0秒或者使用了cookie或者Vary,則不緩存返回給客戶端 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)") { set beresp.ttl = 300s; } elseif (req.url ~ "\.(gif|jpg|jpeg|png)") { set beresp.ttl = 3600s; } elseif (req.url ~ "\.ico") { set beresp.ttl = 10d; } } return (deliver); } sub vcl_deliver { # 返回客戶端以前,增長一個header信息 if (obj.hits > 0) { set resp.http.X-Cache = "Hit from cache"; } else { set resp.http.X-Cache = "Miss from cache"; } # 返回客戶端以前,根據不一樣的瀏覽器類型,增長一個header信息 if (req.http.User-Agent ~ "iPad") { set resp.http.X-Device = "U R iPad"; } elseif (req.http.User-Agent ~ "iPhone") { set resp.http.X-Device = "U R iPhone"; } elseif (req.http.User-Agent ~ "Android") { set resp.http.X-Device = "U R Android"; } return (deliver); }
2.3.5 啓動服務
# service varnish start Starting Varnish Cache: [ OK ] # ss -tnlp | grep varnish LISTEN 0 128 :::80 :::* users:(("varnishd",1299,9)) LISTEN 0 128 *:80 *:* users:(("varnishd",1299,8)) LISTEN 0 10 127.0.0.1:6082 *:* users:(("varnishd",1297,7))
能夠看到,Varnish監聽80端口等待用戶請求。監聽在127.0.0.1的6082端口,這是管理端口。
加載vcl
# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 varnish> vcl.load t1 test.vcl 200 VCL compiled. varnish> vcl.use t1 200 varnish> vcl.list 200 available 0 boot active 0 t1
2.4 測試
2.4.1 靜態頁面
1)WEB1的緩存命中
2)WEB2的緩存命中
2.4.2 動態頁面
php頁面怎麼刷新都不會緩存,緩存不會命中。印證了VCL中設定的策略。
和上面測試php頁面的時候對比,多了響應首部的header。
2.4.3 移動終端設備測試
可使用req.http.User-Agent來判斷瀏覽器類型、操做系統平臺,來對不一樣客戶端用戶提供不一樣的響應方式。固然User-Agent信息能夠僞造,另當別論。
實際上,能夠在vcl_recv中就根據User-Agent來調度到不一樣的後端服務器去處理比較好,這些服務器爲不一樣的移動終端設備優化了頁面,採用一些輕量級的或響應式框架,提高用戶體驗。畢竟手機的移動終端不是計算機,性能有限。
模擬IPhone 5
上圖左側是模擬的IPhone手機,右側是獲取的首部信息,多了一條X-Device。
2.4.4 健康狀態檢測
經過probe定義檢測的頁面、檢測時間間隔、超時時長、檢測的次數、檢測狀態變化的閾值、指望的響應狀態碼等參數,來判斷後端主機的健康情況。
上圖清楚的看到,WEB1由失敗轉爲成功,檢測達到了3次成功後,WEB1才被認爲處於正常的狀態。
2.4.5 PURGE測試
使用curl命令構造一個特殊的請求方法PURGE,未能命中而返回了錯誤。後面就緩存清除有詳細說明。
3. VCL說明
爲了方便,VCL中註釋基本上已經寫得很詳細了。
3.1 後端服務器
backend 使用指令指定後端服務器
.host 指定後端主機的IP地址或者FQDN
.port 指定後端主機的監聽端口
.probe 指定後端主機的健康狀態檢測參數,可使用probe指令單獨定義,這樣能夠被多處調用。固然也能夠以下定義:
backend one { .host = "example.com"; .probe = { .url = "/healthtest"; .interval = 3s; .window = 5; .threshold = 2; } }
3.2 服務器組
director 定義服務器組並指定調度方法,random或者round-robin等,實際一旦緩存啓用,資源在緩存命中,採用什麼調度方法就不是很是重要了。
定義服務器的成員主機,格式上能夠把後端主機定義部分的連同大括號放在這裏,這種寫法實際是相似某些編程語言中的匿名函數,就是符合某種語法規則的未命名的語句塊。固然等號後面只能是其能接受的指令對應格式的語句體。
例如
backend one { .host = "localhost"; .port = "80"; } backend two { .host = "127.0.0.1"; .port = "81"; } director localhosts round-robin { { .backend = one; } { .backend = two; } { .backend = { .host = "localhost"; .port = "82"; } } } sub vcl_recv { set req.backend = localhosts; }
這裏第三個backend就是採用匿名的方式定義在director中,最後vcl_recv中設置req.backend爲localhosts。
3.3 acl訪問控制列表
這種列表是把主機或者網絡組織在一個列表中,方便匹配。
3.4 緩存清除方法
1)purge方法
構建了一個特殊的http方法PURGE,當Varnish收到後就和訪問控制列表acl比對。
若是不在列表中將返回一個不被容許的錯誤給客戶端。
若是在列表中,說明被容許清除緩存。
注意這裏由於是清除緩存,因此下一個函數應該是vcl_hash,若是命中,則說明此對象緩存過,則清除obj對應的全部變量,並直接返回構建錯誤響應報文;若是未命中,則直接返回錯誤構建錯誤報文返回客戶端。
若是vcl_recv以後直接到vcl_pass,這將繞過vcl_hash,這就會繞過緩存,就沒法清除已經緩存的對象。所以沒有使用return(pass),而是使用的return(lookup)。
2)ban方法
可使用ban,這樣varnish並不立刻刪除對應的緩存,而是把url追加到ban list中,等用戶請求到來並命中後,則設置obj.ttl=0,強制過時並刪除obj對象。
緩存的清除須要構建特殊的http方法,並且只能逐個進行,可使用purge和ban方法。若是須要批量清除能夠構建一個清除的文件列表,固然此列表能夠存在數據庫中,而後由腳本或者程序來讀取列表發起方法爲PURGE的http請求,來逐個清除指定文件的緩存。
四、Varnish優化
登陸到Varnish的管理界面,輸入
# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 varnish> param.show
有四個參數
thread_pool_max 3.0後,用來設定單個線程池的最大線程數。
thread_pool_min 用來設定單個線程池的最小線程數
thread-pools 設置線程池的數量,通常和CPU的數目相同最好。
thread_pool_timeout 設置線程超時過時的時間。主要是指線程空閒後,空閒進程總數又大於thread_pool_min,那麼這些空閒線程超時後就被釋放。
官方文檔建議線程總數不要超過5000個。
應根據硬件性能和業務訪問特徵調節這幾個參數。
5. 總結
經過以上的實驗和分析,熟悉了Varnish的基本應用,vcl是控制Varnish的關鍵,同時還有些系統參數和Varnish參數須要調整。
清理緩存須要使用一些特殊構造的http方法,還須要編寫腳本或者編寫程序來完成對批量的指定的文件的緩存清理。
參考資料
https://www.varnish-software.com/static/book/
Varnish 3.x版本下載
https://repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/
本文出自 「終南山下」 博客,謝絕轉載,請與原做者聯繫!