1.介紹
http是現在服務中用的主流協議
restful
緩存 :
代理緩存-----proxy---類似于遞歸
旁掛式緩存-----類似于迭代
(遞歸:直接給出答案,不知道的被人去找答案)
(迭代:自己一級一級問)
緩存有過期機制和條件式請求
vanish具有代理式緩存
正向代理:代表客戶端發請求 client
反向代理:代表服務器端 server
httpd既能做正向代理又能做反向代理
Nginx核心是代理,也可以緩存
squid : httpd
varnish : Nginx
Nginx用epoll做事件驅動
c10k:并發連接 connections 10k
c100k
vanish工作在客戶端和服務器之間作反向代理,并且用作緩存
mysql關系型數據庫
運維日常:
2.Web Page Cache:
squid --> varnish
程序的運行具有局部性特征:
時間局部性:一個數據被訪問過之后,可能很快會被再次訪問到
空間局部性:一個數據被訪問時,其周邊的數據也有可能被訪問到
cache:命中
熱區:局部性;(就像某寶推薦的商品有時效性和局部性)
時效性:
緩存空間耗盡:LRU,最近最少使用;
過期:緩存清理
緩存命中率:hit/(hit+miss) (命中次數/命中次數+未命中次數)
(0,1) 命中次數+為命中次數不一定等于總次數,有的不經過緩存訪問
頁面命中率:基于頁面數量進行衡量
字節命中率: 基于頁面的體積進行衡量
緩存與否:
私有數據:private,private cache;
公共數據:public,public or private cache;
Cache-related Headers Fields 緩存的區域性
The most important caching header fields are:
Expires:過期時間;
Expires:Thu, 22 Oct 2026 06:34:30 GMT
Cache-Control:max-age=
Etag
If-None-Match
Last-Modified
If-Modified-Since
Vary
Age
緩存有效性判斷機制:
過期時間:Expires
HTTP\/1.0
Expires:過期
HTTP\/1.1
Cache-Control: maxage=
Cache-Control: s-maxage=
條件式請求:
Last-Modified/If-Modified-Since:基于文件的修改時間戳來判別;
Etag\/If-None-Match:基于文件的校驗碼來判別;
Expires:Thu, 13 Aug 2026 02:05:12 GMT
Cache-Control:max-age=315360000
ETag:"1ec5-502264e2ae4c0"
Last-Modified:Wed, 03 Sep 2014 10:00:27 GMT
緩存層級:
私有緩存:用戶代理附帶的本地緩存機制;
公共緩存:反向代理服務器的緩存功能;
User-Agent <--> private cache <--> public cache <--> public cache 2 <--> Original Server
請求報文用于通知緩存服務如何使用緩存響應請求:
(以下是請求報文可用的選項)
cache-request-directive =
"no-cache",
| "no-store"
| "max-age" "=" delta-seconds
| "max-stale" [ "=" delta-seconds ]
| "min-fresh" "=" delta-seconds
| "no-transform"
| "only-if-cached"
| cache-extension
響應報文用于通知緩存服務器如何存儲上級服務器響應的內容:
(以下是響應報文可用的選項)
cache-response-directive =
"public"
| "private" [ "=" <"> 1#field-name <"> ]
| "no-cache" [ "=" <"> 1#field-name <"> ],可緩存,但響應給客戶端之前 條件式請求進行緩存有效性驗正;
| "no-store" ,不允許存儲響應內容于緩存中;
| "no-transform"
| "must-revalidate"
| "proxy-revalidate"
| "max-age" "=" delta-seconds 定義最大緩存時長,過期時間
| "s-maxage" "=" delta-seconds 定義最大緩存時長,僅用于控制公共時長
| cache-extension
開源解決方案:
squid:
varnish:
varnish官方站點: http://www.varnish-cache.org/
Community
Enterprise
This is Varnish Cache, a high-performance HTTP accelerator.
varnish2.0,3.0處理過程
varnish4.0
varnish的程序環境:(我們只能緩存GET和HEAD請求)s
3.vanish程序架構:
vanish由manager和cacher進程組成,還有共享內存日志組件
Manager進程(主控進程)
Cacher進程,包含多種類型的線程:
accept, worker, expiry, ...
(cacher處理各種緩存事物,比如處理請求,管理緩存,清理過期緩存)
shared memory log:
( 共享內存日志:為了免得日志成為性能瓶頸,日志信息直接計入內存)
統計數據:計數器;
日志區域:日志記錄;
varnishlog, varnishncsa, varnishstat...
配置接口:VCL
Varnish Configuration Language,
vcl complier --> c complier --> shared object
/etc/varnish/varnish.params:配置varnish服務進程的工作特性,例如監聽的地址和端口,緩存機制;
/etc/varnish/default.vcl:配置各Child/Cache線程的緩存策略;
主程序:
/usr/sbin/varnishd
CLI interface:
/usr/bin/varnishadm
Shared Memory Log交互工具:
/usr/bin/varnishhist
/usr/bin/varnishlog
/usr/bin/varnishncsa
/usr/bin/varnishstat
/usr/bin/varnishtop
測試工具程序:
/usr/bin/varnishtest
VCL配置文件重載程序:
/usr/sbin/varnish_reload_vcl
Systemd Unit File:
/usr/lib/systemd/system/varnish.service
varnish服務
/usr/lib/systemd/system/varnishlog.service
/usr/lib/systemd/system/varnishncsa.service
日志持久的服務;
varnish的緩存存儲機制( Storage Types):
-s [name=]type[,options]
· malloc[,size]
內存存儲,[,size]用于定義空間大小;重啟后所有緩存項失效;
· file[,path[,size[,granularity]]]
磁盤文件存儲,黑盒;重啟后所有緩存項失效;
· persistent,path,size
文件存儲,黑盒;重啟后所有緩存項有效;實驗;暫不能用
varnish程序的選項:
程序選項:/etc/varnish/varnish.params文件
-a address[:port][,address[:port][...],默認為6081端口;
-T address[:port],默認為6082端口;
-s [name=]type[,options],定義緩存存儲機制;
-u user
-g group
-f config:VCL配置文件;
-F:運行于前臺;
...
運行時參數:/etc/varnish/varnish.params文件, DEAMON_OPTS
DAEMON_OPTS="-p thread_pool_min=5 -p thread_pool_max=500 -p thread_pool_timeout=300"
-p param=value:設定運行參數及其值; 可重復使用多次;
-r param[,param...]: 設定指定的參數為只讀狀態;
clipboard6.png
重載vcl配置文件:
~ ]# varnish_reload_vcl
# varnishadm(varnish客戶端命令)
-S /etc/varnish/secret -T [ADDRESS:]PORT
help [<command>]
ping [<timestamp>]
auth <response>
quit
banner
status
start
stop
vcl.load <configname> <filename> 相當于varnish_relod_acl:加載ACL
vcl.inline <configname> <quoted_VCLstring>
vcl.use <configname>
vcl.discard <configname>
vcl.list
param.show [-l] [<param>]
param.set <param> <value>
panic.show
panic.clear
storage.list
vcl.show [-v] <configname>
backend.list [<backend_expression>]
backend.set_health <backend_expression> <state>
ban <field> <operator> <arg> [&& <field> <oper> <arg>]...
ban.list
clipboard7.png
配置文件相關:
vcl.list
vcl.load:裝載,加載并編譯;
vcl.use:激活;
vcl.discard:刪除;
vcl.show [-v] <configname>:查看指定的配置文件的詳細信息;
運行時參數:
param.show -l:顯示列表;
param.show <PARAM>
param.set <PARAM> <VALUE>
緩存存儲:
storage.list
后端服務器:
backend.list
VCL:
”域“專有類型的配置語言;
state engine:狀態引擎;
VCL有多個狀態引擎,狀態之間存在相關性,但狀態引擎彼此間互相隔離;每個狀態引擎可使用return(x)指明關聯至哪個下一級引擎;每個狀態引擎對應于vcl文件中的一個配置段,即為subroutine
vcl_hash --> return(hit) --> vcl_hit
vcl_recv的默認配置:
sub vcl_recv {
if (req.method == "PRI") {
/* We do not support SPDY or HTTP/2.0 */
return (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);
}
if (req.method != "GET" && req.method != "HEAD") {
/* We only deal with GET and HEAD by default */
return (pass);
}
if (req.http.Authorization || req.http.Cookie) {
/* Not cacheable by default */
return (pass);
}
return (hash);
}
}
Client Side:
vcl_recv, vcl_pass, vcl_hit, vcl_miss, vcl_pipe, vcl_purge, vcl_synth, vcl_deliver
vcl_recv:
hash:vcl_hash
pass: vcl_pass
pipe: vcl_pipe
synth: vcl_synth
purge: vcl_hash --> vcl_purge
vcl_hash:
lookup:
hit: vcl_hit
miss: vcl_miss
pass, hit_for_pass: vcl_pass
purge: vcl_purge
Backend Side:
vcl_backend_fetch, vcl_backend_response, vcl_backend_error
兩個特殊的引擎:
vcl_init:在處理任何請求之前要執行的vcl代碼:主要用于初始化VMODs;
vcl_fini:所有的請求都已經結束,在vcl配置被丟棄時調用;主要用于清理VMODs;
vcl的語法格式:
(1) VCL files start with vcl 4.0;表明版本
(2) //, # and /* foo / for comments;//,#代表單行注釋 / */代表多行注釋
(3) Subroutines are declared with the sub keyword; 例如sub vcl_recv { ...};
每個子例程以sub關鍵字開頭
(4) No loops, state-limited variables(受限于引擎的內建變量);不支持循環
(5) Terminating statements with a keyword for next action as argument of the return() function, i.e.: return(action);用于實現狀態引擎轉換;
(6) Domain-specific;
The VCL Finite State Machine
(1) Each request is processed separately;每個請求都是獨立的
(2) Each request is independent from others at any given time;每個請求都與 其他的請求分離
(3) States are related, but isolated;狀態是有關聯的
(4) return(action); exits one state and instructs Varnish to proceed to the next state;return退出當前狀態
(5) Built-in VCL code is always present and appended below your own VCL;
Built-in VCL 相當于默認vcl
三類主要語法:
sub subroutine {
...
}
if CONDITION {
...
} else {
...
}
return(), hash_data()
VCL Built-in Functions and Keywords
函數:
regsub(str, regex, sub)
regsuball(str, regex, sub)
ban(boolean expression)
hash_data(input)
synthetic(str)
Keywords:
call subroutine, return(action),new,set,unset
操作符:
==, !=, ~, >, >=, <, <=
邏輯操作符:&&, ||, !
變量賦值:=
舉例:obj.hits是內建變量,用于保存某緩存項的從緩存中命中的次數;
if (obj.hits>0) {
set resp.http.X-Cache = "HIT via "(固定字符串) + server.ip;
} else {
set resp.http.X-Cache = "MISS from "(未能命中) + server.ip;
}
clipboard11.png
常用變量:
bereq., req.:
bereq.http.HEADERS
bereq.request:請求方法;
bereq.url:請求的url;
bereq.proto:請求的協議版本;
bereq.backend:指明要調用的后端主機;
req.http.Cookie:客戶端的請求報文中Cookie首部的值;
req.http.User-Agent ~ "chrome"
beresp., resp.:
beresp.http.HEADERS
beresp.status:響應的狀態碼;
reresp.proto:協議版本;
beresp.backend.name:BE主機的主機名;
beresp.ttl:BE主機響應的內容的余下的可緩存時長;
obj.*
obj.hits:此對象從緩存中命中的次數;
obj.ttl:對象的ttl值,緩存過期
server.*
server.ip:varnish主機的IP;
server.hostname:varnish主機的Hostname
;
client.*
client.ip:發請求至varnish主機的客戶端IP;
用戶自定義:
set
unset
示例1:強制對某類資源的請求不檢查緩存:
vcl_recv {
if (req.url ~ "(?i)^/(login|admin)") { (?i)表示忽略大小寫,后面是匹配URI,
不包括主機名和端口
return(pass);
}
}
clipboard14.png
vcl.show test2 查看內容
vcl.use test2
示例2:對于特定類型的資源,例如公開的圖片等,取消其私有標識,并強行設定其可以由varnish緩存的時長; 定義在vcl_backend_response中;
(并不是所有的cookie都不可以被緩存下來,可以剝離cookie)
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;
}
}
示例3:定義在vcl_recv中;
if (req.restarts == 0) { 請求的重啟次數為0
if (req.http.X-Fowarded-For) { 如果請求報文中有forwarded-for
set req.http.X-Forwarded-For = req.http.X-Forwarded-For + "," + client.ip;
} else {
set req.http.X-Forwarded-For = client.ip;
}
}
后端主機# vim /etc/httpd/conf/htttpd.conf
clipboard15.png
purge:手動修剪指定的緩存項
ban:一類的緩存項
(1) 能執行purge操作
sub vcl_purge {
return (synth(200,"Purged"));
}
(2) 何時執行purge操作
sub vcl_recv {
if (req.method == "PURGE") {
return(purge);
}
...
}
添加此類請求的訪問控制法則:
acl purgers {
"127.0.0.0"/8;
"10.1.0.0"/16;
}
sub vcl_recv {
if (req.method == "PURGE") {
if (!client.ip ~ purgers) {
return(synth(405,"Purging not allowed for " + client.ip));
}
return(purge);
}
...
}
clipboard16.png
Banning:
(1) varnishadm:
ban <field> <operator> <arg>
示例:
ban req.url ~ ^/javascripts
(2) 在配置文件中定義,使用ban()函數;
示例:
if (req.method == "BAN") {
ban("req.http.host == " + req.http.host + " && req.url == " + req.url);
# Throw a synthetic page so the request won't go to the backend.
return(synth(200, "Ban added"));
}
ban req.http.host==www.ilinux.io && req.url==/test1.html
如何設定使用多個后端主機:
backend default {每添加一個后端主機就寫一個backend,default代表一個主機名
.host = "172.16.100.6";真正主機的地址
.port = "80";真正主機的端口
}
backend appsrv {
.host = "172.16.100.7";
.port = "80";
}
sub vcl_recv {
if (req.url ~ "(?i).php$") {
set req.backend_hint = appsrv;
} else {
set req.backend_hint = default;
}
...
}
clipboard18.png
Director:
varnish module;
使用前需要導入:
import directors;
varnish兩種調度算法輪詢和隨機
示例:
import directors; # load the directors
backend server1 {
.host =
.port =
}
backend server2 {
.host =
.port =
}
sub vcl_init {
new GROUP_NAME = directors.round_robin();
GROUP_NAME.add_backend(server1);
GROUP_NAME.add_backend(server2);
}
sub vcl_recv {
# send all traffic to the bar director:
set req.backend_hint = GROUP_NAME.backend();組名
}
clipboard22.png
會話保存的三種方式:
會話綁定 基于原IP綁定,基于應用層綁定
session復制
session服務器
基于cookie的session sticky:
sub vcl_init {
new h = directors.hash();
h.add_backend(one, 1); // backend 'one' with weight '1'
h.add_backend(two, 1); // backend 'two' with weight '1'
}
sub vcl_recv {
// pick a backend based on the cookie header of the client
set req.backend_hint = h.backend(req.http.cookie);
}
BE Health Check:
backend BE_NAME {
.host =
.port =
.probe = {
.url=
.timeout=
.interval=
.window=
.threshold=
}
}
.probe:定義健康狀態檢測方法;
.url:檢測時要請求的URL,默認為”/";
.request:發出的具體請求;
.request =
"GET /.healthtest.html HTTP/1.1"
"Host: www.magedu.com"
"Connection: close"
.window:基于最近的多少次檢查來判斷其健康狀態;
.threshold:最近.window中定義的這么次檢查中至有.threshhold定義的次數是成功;
.interval:檢測頻度;每個多長時間檢查一次
.timeout:超時時長;
.expected_response:期望的響應碼,默認為200;
clipboard23.png
健康狀態檢測的配置方式:
(1) probe PB_NAME { }
backend NAME = {
.probe = PB_NAME;
...
}
(2) backend NAME {
.probe = {
...
}
}
示例:
probe check {
.url = "/.healthcheck.html";
.window = 5;
.threshold = 4;
.interval = 2s;
.timeout = 1s;
}
backend default {
.host = "10.1.0.68";
.port = "80";
.probe = check;
}
backend appsrv {
.host = "10.1.0.69";
.port = "80";
.probe = check;
}
手動設定BE主機的狀態:
sick:管理down;
healthy:管理up;
auto:probe auto;
clipboard24.png
設置后端的主機屬性:
backend BE_NAME {
...
.connect_timeout = 0.5s;
.first_byte_timeout = 20s;
.between_bytes_timeout = 5s;兩個字節之間傳送的間隔,如果超時也認為down
.max_connections = 50;
}
varnish的運行時參數:
線程模型:
cache-worker
cache-main
ban lurker
acceptor:
epoll/kqueue:
...
線程相關的參數:使用線程池機制管理線程;
在線程池內部,其每一個請求由一個線程來處理; 其worker線程的最大數決定了varnish的并發響應能力;
每個參數都要使用-p來引導
thread_pools:Number of worker thread pools. 最好小于或等于CPU核心數量;
thread_pool_max:每線程池的最大線程數;
thread_pool_min:The minimum number of worker threads in each pool. 額外意義為“最大空閑線程數”;
最大并發連接數 = thread_pools * thread_pool_max
thread_pool_timeout:Thread idle threshold. Threads in excess of thread_pool_min, which have been idle for at least this long, will be destroyed.
thread_pool_add_delay:Wait at least this long after creating a thread.添加線程延遲一段時間,使用默認值就好
thread_pool_destroy_delay:Wait this long after destroying a thread.
Timer相關的參數:
send_timeout:Send timeout for client connections. If the HTTP response hasn't been transmitted in this many seconds the session is closed.
timeout_idle:Idle timeout for client connections.
timeout_req: Max time to receive clients request headers, measured from first non-white-space character to double CRNL.
cli_timeout:Timeout for the childs replies to CLI requests from the mgt_param.
設置方式:
vcl.param
param.set
永久有效的方法:
varnish.params
DEAMON_OPTS="-p PARAM1=VALUE -p PARAM2=VALUE"
varnish運行時參數,重啟緩存將失效
clipboard25.png
varnish日志區域:
shared memory log
計數器
日志信息
1、varnishstat - Varnish Cache statistics
-1 表示只顯示一批就結束
-1 -f FILED_NAME
-f FILED_NAME 查看某一個字段
-l:可用于-f選項指定的字段名稱列表;
MAIN.cache_hit
MAIN.cache_miss 沒有命中
# varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss
顯示指定參數的當前統計數據;
# varnishstat -l -f MAIN -f MEMPOOL
列出指定配置段的每個參數的意義;
clipboard26.png
2、varnishtop - Varnish log entry ranking
-1 Instead of a continously updated display, print the statistics once and exit.
-i taglist,可以同時使用多個-i選項,也可以一個選項跟上多個標簽;
-I <[taglist:]regex>
-x taglist:排除列表,出了什么其他的都顯示
-X <[taglist:]regex>
3、varnishlog - Display Varnish logs
4、 varnishncsa - Display Varnish logs in Apache / NCSA combined log format
內建函數:
hash_data():
指明哈希計算的數據;減少差異,以提升命中率;
regsub(str,regex,sub):
把str中被regex第一次匹配到字符串替換為sub;主要用于URL Rewrite
regsuball(str,regex,sub):
把str中被regex每一次匹配到字符串均替換為sub;
return():
ban(expression)
ban_url(regex):
Bans所有的其URL可以被此處的regex匹配到的緩存對象;
synth(status,"STRING"):purge操作;
總結
varnish: state engine, vcl
varnish 4.0:
vcl_init
vcl_rec
vcl_hash
vcl_hit
vcl_pass
vcl_miss
vcl_pip
vcl_waiting
vcl_purge
vcl_deliver
vcl_synth
vcl_fini
vcl_backend_fetch
vcl_backend_response
vcl_backend_error
sub VCL_STATE_ENGINE {
...
}
backend BE_NAME {}
probe PB_NAME {}acl ACL_NAME {}
實戰項目:兩個lamp部署wordpress,用Nginx反代,做壓測;nginx后部署varnish緩存,調整vcl,多次壓測;
ab, http_load, webbench, seige, jmeter, loadrunner,...
補充資料:varnish book
http://book.varnish-software.com/4.0/
示例:
backend imgsrv1 {
.host = "192.168.10.11";
.port = "80";
}
backend imgsrv2
.host = "192.168.10.12";
.port = "80";
}
backend appsrv1 {
.host = "192.168.10.21";
.port = "80";
}
backend appsrv2 {
.host = "192.168.10.22";
.port = "80";
}
sub vcl_init {
new imgsrvs = directors.random();
imgsrvs.add_backend(imgsrv1,10);
imgsrvs.add_backend(imgsrv2,20);
new staticsrvs = directors.round_robin();
appsrvs.add_backend(appsrv1);
appsrvs.add_backend(appsrv2);
new appsrvs = directors.hash();
appsrvs.add_backend(appsrv1,1);
appsrvs.add_backend(appsrv2,1);
}
sub vcl_recv {
if (req.url ~ "(?i).(css|js)$" {
set req.backend_hint = staticsrvs.backend();
}
if (req.url ~ "(?i).(jpg|jpeg|png|gif)$" {
set req.backend_hint = imgsrvs.backend();
} else {
set req.backend_hint = appsrvs.backend(req.http.cookie);
}
}