一、varnish学习
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 <"> ],可缓存,但响应给客户端之前需要revalidation,即必须发出条件式请求进行缓存有效性验正; | "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. 程序架构: Manager进程 Cacher进程,包含多种类型的线程: accept, worker, expiry, ... shared memory log: 统计数据:计数器; 日志区域:日志记录; varnishlog, varnishncsa, varnishstat... 配置接口:VCL Varnish Configuration Language, vcl complier --> c complier --> shared object varnish的程序环境: /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...]: 设定指定的参数为只读状态; 重载vcl配置文件: ~ ]# varnish_reload_vcl varnishadm -S /etc/varnish/secret -T [ADDRESS:]PORT help [<command>] ping [<timestamp>] auth <response> quit banner status start stop vcl.load <configname> <filename> 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 配置文件相关: 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 { ...}; (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; (5) Built-in VCL code is always present and appended below your own 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 via " + server.ip; } 变量类型: 内建变量: 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:请求方法; 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 server.hostname client.* client.ip 用户自定义: set unset 示例1:强制对某类资源的请求不检查缓存: vcl_recv { if (req.url ~ "(?i)^/(login|admin)") { #(?i)表示不区分大小写。 return(pass); } } 然后在终端的vcl-cli交互工具执行:vcl.load test1 default.vcl即可。 示例2:对于特定类型的资源,例如公开的图片等,取消其私有标识,并强行设定其可以由varnish缓存的时长; 定义在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; } } 示例3:定义在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, 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); } ... } 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")); } 如何设定使用多个后端主机: 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; } ... } Director: varnish module; 使用前需要导入: import directors; 示例: 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(); } 基于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; 健康状态检测的配置方式: (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; } 设置后端的主机属性: backend BE_NAME { ... .connect_timeout = 0.5s; .first_byte_timeout = 20s; .between_bytes_timeout = 5s; .max_connections = 50; } varnish的运行时参数: 线程模型: cache-worker cache-main ban lurker acceptor: epoll/kqueue: ... 线程相关的参数: 在线程池内部,其每一个请求由一个线程来处理; 其worker线程的最大数决定了varnish的并发响应能力; thread_pools:Number of worker thread pools. 最好小于或等于CPU核心数量; thread_pool_max:The maximum number of worker threads in each pool. 每线程池的最大线程数; 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日志区域: shared memory log 计数器 日志信息 1、varnishstat - Varnish Cache statistics -1 -1 -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 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_recv vcl_hash vcl_hit vcl_pass vcl_miss vcl_pipe 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,多次压测; 课外实践:(1) zabbix监控varnish业务指标; (2) ansible实现varnish快速部署; 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); } }
学习varnish的不错的网站
https://blog.51cto.com/harisxiong/1556778
https://www.cnblogs.com/heiye123/articles/7774146.html
https://blog.51cto.com/xinzong/1782669
https://www.jianshu.com/p/1d4918cc58e8
二、简述回源原理和CDN常见多级缓存 1. 回源原理 回源是指浏览器在发送请求报文时,响应该请求报文的是源站点的服务器,而不是各节点上的缓存服务器,那么这个过程相对于通过各节点上的缓存服务器来响应的话就称作为回源。回源的请求或流量太多的话,有可能会让源站点的服务器承载着过大的访问压力,进而影响服务的正常访问。 常规的CDN都是回源的。即:当有用户访问某一个URL的时候,如果被解析到的那个CDN节点没有缓存响应的内容,或者是缓存已经到期,就会回源站去获取。如果没有人访问,那么CDN节点不会主动去源站拿的。 源站内容有更新的时候,源站主动把内容推送到CDN节点。 2. CDN缓存 (1)CDN:缓存网络,Content Delivery Network,即内容分发网络;加速器,向代理缓存。 (2)基本思路 尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决Internet网络拥挤状况,提高用户访问网站的响应速度。 (3)CDN的基本原理 广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求。 (4)服务模式 内容分发网络(CDN)是一种新型网络构建方式,它是为能在传统的IP网发布宽带丰富媒体而特别优化的网络覆盖层;而从广义的角度,CDN代表了一种基于质量与秩序的网络服务模式。 简单地说,内容分发网络(CDN)是一个经策略性部署的整体系统,包括分布式存储、负载均衡、网络请求的重定向和内容管理4个要件,而内容管理和全局的网络流量管理(Traffic Management)是CDN的核心所在。通过用户就近性和服务器负载的判断,CDN确保内容以一种极为高效的方式为用户的请求提供服务。 总的来说,内容服务基于缓存服务器,也称作代理缓存(Surrogate),它位于网络的边缘,距用户仅有"一跳"(Single Hop)之遥。同时,代理缓存是内容提供商源服务器(通常位于CDN服务提供商的数据中心)的一个透明镜像。这样的架构使得CDN服务提供商能够代表他们客户,即内容供应商,向最终用户提供尽可能好的体验,而这些用户是不能容忍请求响应时间有任何延迟的。 (5)关键技术 1.内容发布:它借助于建立索引、缓存、流分裂、组播(Multicast)等技术,将内容发布或投递到距离用户最近的远程服务点(POP)处; 2.内容路由:它是整体性的网络负载均衡技术,通过内容路由器中的重定向(DNS)机制,在多个远程POP上均衡用户的请求,以使用户请求得到最近内容源的响应; 3.内容交换:它根据内容的可用性、服务器的可用性以及用户的背景,在POP的缓存服务器上,利用应用层交换、流分裂、重定向(ICP、WCCP)等技术,智能地平衡负载流量; 4.性能管理:它通过内部和外部监控系统,获取网络部件的状况信息,测量内容发布的端到端性能(如包丢失、延时、平均带宽、启动时间、帧速率等),保证网络处于最佳的运行状态。 (6)主要特点 1、本地Cache加速 提高了企业站点(尤其含有大量图片和静态页面站点)的访问速度,并大大提高以上性质站点的稳定性 2、镜像服务 消除了不同运营商之间互联的瓶颈造成的影响,实现了跨运营商的网络加速,保证不同网络中的用户都能得到良好的访问质量。 3、远程加速 远程访问用户根据DNS负载均衡技术智能自动选择Cache服务器,选择最快的Cache服务器,加快远程访问的速度 4、带宽优化 自动生成服务器的远程Mirror(镜像)cache服务器,远程用户访问时从cache服务器上读取数据,减少远程访问的带宽、分担网络流量、减轻原站点WEB服务器负载等功能。 5、集群抗攻击 广泛分布的CDN节点加上节点之间的智能冗余机制,可以有效地预防黑客入侵以及降低各种D.D.o.S攻击对网站的影响,同时保证较好的服务质量 。 (7)GSLB:全局服务负载均衡器; 调度客户请求到不同的缓存服务器上; (8)SLB:局部负载均衡器;