5.3 Varnish配置
Varnish状态引擎处理流程如图5-2所示。
图5-2 Varnish状态引擎处理流程图
对图5-2所示的流程分析如下。
vcl_recv:用于接收并处理用户请求,当接收到一个完整的请求时,它会检查并分析是否可以为这个请求服务,判断请求的数据并决定如何处理请求。vcl_recv可以选择多个策略,如1.pipe、2.lookup、3.pass。它会将控制权传递给下游,如vcl_pipe、vcl_hash、vcl_pass。
- 1.1 vcl_pipe:不会缓存数据,其会进入pipe模式,由管道后端处理数据,直到管道处理完毕后关闭。
- 2.1 vcl_hash:缓存数据,通过hash处理机制,默认URL可作为key,key的方式可自定义调整,同时可以根据客户端是否区分压缩数据而进一步判断是否存储缓存。
- 2.2 vcl_hit:一个请求从缓存中命中需要的内容。
- 2.3 vcl_miss:一个请求从缓存中未命中需要的内容。
- 2.4/2.5 vcl_pass:对命中或未命中的数据提供数据处理功能。
- 2.8 vcl_fetch:从后端服务器获得请求目标数据。
- 2.9 deliver:从后端服务器获得数据后,根据策略检查是否需要缓存起来。
- 3.1/3.2 vcl_deliver:内容返回给客户端。
对图5-2所示流程中涉及的函数介绍如下。
- pipe:将请求交给vcl_pipe函数。error code[reason]表示返回code给客户端并放弃请求,code是错误标示,例如200、405等,reason是错误原因。
- vcl_pipe:该函数在进入pipe模式时被调用,用于将请求直接传递给后端主机,在请求和返回内容没有改变的情况下,将不变的内容返回给客户端,直到这个链接关闭。
- vcl_pass:该函数在进入pass模式时被调用,用于直接将请求发送给后端主机,后端主机响应后发送给客户端,不进行任何缓存,每次都返回最新内容。
- vcl_hash:可缓存数据,通过hash机制处理,默认将URL作为key;也可以自定义,根据客户端是否支持处理压缩数据来区分缓存。
- lookup:在缓存中查找被请求的对象,并且根据查找的结果交给vcl_hit函数(命中)或vcl_miss函数(未命中)处理。
- vcl_hit:在执行lookup后,如果在缓存中命中对象,该函数将会被自动调用。
- deliver:表示找到内容并发送给客户端,把控制权交给vcl_deliver函数。
- vcl_miss:在执行lookup后,如果缓存中没有命中对象,该函数会被调用,可用于判断是否从后端请求内容。
- fetch:表示从后端获取内容,并把控制权交给vcl_fetch函数。
- vcl_fetch:从后端主机更新缓存并获取内容后调用该函数,接着判断获取的内容是放入缓存还是直接给客户端。
- vcl_deliver:将在缓存中找到的内容发送给客户端调用的方法。
下面介绍Varnish的配置文件(default.vcl),如代码清单5-3所示。
代码清单5-3 default.vcl配置文件
probe tz1{ .url="/demo/xxxx.index.html"; // 检查后端健康页面 .timeout=0.3s; // 过期时间 .window=8; // 检查后端服务次数 .threshod=3; // 检查后端8次访问,若成功3次则认为服务是存活的 .initial=3; // Varnish启动,确保多少个probe正常 .expected_response=200; // 期望expected code,默认是200 .interval=6; // 定义probe多久检查一次后端,默认为5s } backend zachary{ .host="127.0.0.1"; .port="2222"; .connect_timeout=1s; .first_byte_timeout=5s; .between_byte.timeout=2s; .max_connections=1000; .probe=tz1; } backend resource{ .host="127.0.0.1"; .port="8099"; .connect_timeout=2s; // 定义等待连接后端的时间 .first_byte_timeout=5s; // 定义等待从backend传输过来的第一个字节的时间 .between_byte.timeout=2s; // 定义两个字节的间隔时间 } director zachary random{ // 随机 .retries=5; // 查找可用后端次数 { .backend=zachary; // 引用已存在的backend .weight=6; } { .backend=resource; // 引用已存在的backend .weight=2; // 类似Nginx权重 } { .backend={ // 定义新的backend } .weight=2; } } director zachary round-robin{ // 轮询 { .backend=zachary; // 引用已存在的backend } { .backend=resource; // 引用已存在的backend } { .backend={ // 定义新的backend } } } #权限访问控制列表 acl purgeallow { "127.0.0.1"; !"192.168.0.102" } sub vcl_recv{ if(!req.backend.healthy){ set req.grace=30m; // Varnish缓存时间+50分钟返回给客户端 }else{ set req.grace=5s; } if (req.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|t bz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") { unset req.http.cookie; return (hash); } if(req.request== "PURGE"){ (!client.ip ~ purgeallow){ error 405 "not allowed"; } return(lookup); } 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.http.Cache-Control ~ "(?i)no-cache") { if (!(req.http.Via || req.http.User-Agent ~ "(?i)bot" || req.http.X-Purge)) { return (purge); } } if(req.http.host ~"^(www.)?zachary.cn$"){ set req.backnd=zachary; } if(req.request== "GET" && req.url ~ "\.(jpg|png|gif|swf|flv|ico|jpeg)$"){ unset req.http.cookie; } if(req.request== "GET" && req.url ~ "(?i)\.jsp($|\?)"){ set req.backnd=resource; return pass(pass); } } sub vcl_fetch{ set beresp.grace=30m; // 后端服务器返回Varnish缓存时间+50分钟 if(req.request== "GET" req.url ~ "\.(jpg|png|gif|swf|flv|ico|jpeg)$"){ set beresp.ttl=1d; } if(req.url ~ "^.*/zachary/demo/.*"){ set beresp.ttl=1d; return(deliver); } } sub vcl_hit{ std.log("url hit,your need to check it; it's url ="+req.url); if(req.request=='PURGE'){ set obj.ttl=0s; // 清除缓存 error 200 "Purged."; } return(fetch); } sub vcl_miss{ std.log("url miss,your need to check it; it's url ="+req.url); if(req.request=='PURGE'){ error 200 "Purged."; } return(fetch); }
对上述代码中的重点流程介绍如下。
1)1个请求对应1个backend,可配置连接后端服务。其中,connect_timeout表示连接后端超时时间,first_byte_timeout表示传输第一个字节的时间,between_byte.timeout表示第二个和第一个中间传输所用的时间,max_connections表示连接后端服务的最大限制数。
2)backend有多种配置策略,如随机、循环、DNS等,可参考代码清单5-3中的相关配置。
3)probe用于配置健康检查。
4)acl用于设置权限列表,可配置相关IP。
当多个客户端请求同时访问一个页面时,Varnish只会发送一次请求到后端,其他请求会被挂起以等待返回结果,体验较差。当服务器请求流量高时,比如在秒杀活动中、同时产生数千万点击率时等,用户不可能挂起等待结果。
针对以上问题,Varnish提供了Grace模式来延长缓存失效时间,即上次过期数据结果在失效时间之后延期多长时间,具体时间需根据系统斟酌设置,当后端服务器出现问题时,负载过高后,Varnish不访问后端直接返回旧缓存数据到客户端。
VCL返回策略:
- return(pass):不缓存,直接调用服务器。
- return(lookup):先从缓存获取,缓存中没有数据再从服务器获取。
- return(pipe):当前连接未关闭前,所有的请求都直接由服务器处理,Varnish不处理请求。
- return(deliver):请求目标被缓存,然后返回客户端。
5.4 Varnish核心指令
5.4.1 Varnish核心指令之backend
backend是用于定义后端服务器的子例程。其具体使用方法如代码清单5-4所示。
代码清单5-4 backend定义子例程
backend zachary{ .host="127.0.0.1"; // 指明后端主机 .port="2222"; .connect_timeout=1s; .first_byte_timeout=5s; .between_byte.timeout=2s; .max_connections=1000; .probe=tz1; } backend resource{ .host="192.168.0.1"; // 指明后端主机 .port="8099"; .connect_timeout=1s; .first_byte_timeout=5s; .between_byte.timeout=2s; }
注意
Varnish允许定义多个backend后端服务器。