varnish详解
Varnish是一款高性能的开源HTTP缓存加速器,从下图(网站拓扑结构视图)不难看出,缓存对于现代互联网的重要意义。基础理论缓存存在的基础程序具有局部性时间局部性过去访问过的数据在一段时间内有可能被再次访问空间局部性过去被访问的某数据周围的其他数据有可能也被访问缓存得以生效就是因为程序的局部性缓存的存储方式缓存以key-value形式存储key:访问路径,url,经过hash计算后存储;value:web content缓存主要缓存的是热点数据缓存命中率hit/(hit+miss)衡量标准文档命中率:以命中文档的个数进行衡量;字节命中率:以命中内容的大小进行衡量;注意事项1、缓存对象有生命周期,所以需定期清理;2、当缓存耗尽时,可基于LRU(最近最少使用)算法,实时清理一部分缓存,以腾出空间存放新的缓存;3、缓存需注意私密性,有些内容不可缓存,如用户的私有数据、报文首部中包含“Authorization,Cookie, Vary: accept-encoding=”等信息的;缓存处理步骤接收请求 --> 解析请求(提取请求的url及各种首部)--> 查询缓存 --> 新鲜度检测 --> 构建响应报文--> 发送响应 --> 记录日志新鲜度检测机制过期机制HTTP/1.0,在报文首部Expires,例如“Expires:Thu, 04 Jun 2015 23:38:18 GMT”,使用的是绝对时间,规定在此时间之后的缓存都是不新鲜的,考虑到时区问题,此种设定有天然缺陷;HTTP/1.1,Cache-Control:max-age,例如“Cache-Control:max-age=600”,使用的是相对时间,规定缓存的最大存活时长;服务器有效性再验证revalidate如果原始内容未改变,则仅响应报文首部(不附带body部分),响应码304(Not Modified);如果原始内容改变,则正常响应,响应码200;如果原始内容消失,则响应404,此时缓存中的cache object也相应被删除;条件式请求首部If-Modified-Since:If-Unmodified-Since:基于请求内容的时间戳作验证;缺陷:若后端服务器内容改变频率为“毫秒”级,时间戳的最小单位为“秒”,此种验证会导致使用过期缓存;If-Match:If-None-Match:对每个生成的内容作一个“Etag”标记,基于此特征码作验证;常见缓存服务器开源解决方案varnish,squid缓存首先必须是一个反向代理varnish基础https://www.varnish-cache.org/varnish工作架构图Management管理进程:编译VCL并应用新配置、监控varnish、初始化varnish并提供命令行接口CLI;Child/Cache线程:Acceptor:用于接收并连接请求;Worker:用于处理并响应用户请求;Object expiry:从缓存中清理过期的cache object;cache-main:此线程只有一个,用于启动cache;banluker:清理指定缓存;expire:清理过期缓存epoll:线程池管理器;varnish定义其最大并发连接数是通过线程池模型实现的:thread_pools:线程池个数,默认为2;thread_pool_max:单线程池内允许启动的最大线程个数;thread_pool_min:单线程池内允许启动的最小线程个数;thread_pool_timeout:多于thread_pool_min的线程空闲此参数指定的时长后即被purge;varnish的param查看及设置方法使用“varnishadm”命令后使用如下命令实现:param.show[-l] param.set Log file:varnish的日志,为保持其高性能,保持在一段共享内存空间中--Shared Memory Log,共享内存日志大小一般为90M,分为两部分:前一部分为计数器,后一部分为客户请求相关的数据,可以计算缓存命中率、分析请求首部、记录响应首部等;vcl:Varnish Configuration Language缓存策略配置工具;基于“域”的简单编程语言,意为其所有代码都是写在各“{}”中,且作用范围仅为对应的“{}”,这些“域”称为varnish的状态引擎;varnish定义其最大并发连接数:线程池模型:thread_pools:线程池个数;默认为2;thread_pool_max:单线程池内允许启动的最多线程个数;thread_pool_minthread_pool_timeout:多于thread_pool_min的线程空闲此参数指定的时长后即被purge;vcl处理流程图state enginevcl配置的缓存策略在这些stateengine上发挥作用;state engine之间有相关性,上级engine通过return指明下级engine;varnish处理请求前要先分析该请求的http首部;vcl_init:在装载vcl,用其处理任何请求之前;vcl_recv请求被接入,但在其被分析、处理完成之前;是否响应此请求、如何响应、使用哪个后端主机响应;vcl_pipevcl_hashvcl_hitvcl_missvcl_passvcl_fetch从backend主机收到响应报文之前被调用;可return的值:deliver、errorcode 、restart;vcl_delivervcl_error配置文件lftp172.16.0.1:/pub/Sources/6.x86_64/varnish> mget *.rpm(源码包位置)varnish-3.0.6-1.el6.x86_64.rpm、varnish-libs-3.0.6-1.el6.x86_64.rpm、varnish-docs-3.0.6-1.el6.x86_64.rpm(此包非必须,仅用于提供各种说明文档)、varnish-libs-devel-3.0.6-1.el6.x86_64.rpm(若不做二次开发,此包无需安装)/etc/varnish/default.vcl配置语法(1) //,#,注释单行,/*comment*/注释多行;(2) sub $NAME用于定义函数,但函数不接受参数;(3) 不支持循环;(4) 支持众多内置变量;(5) 支持使用终止语句,但没有返回值;(6) “域”专用语言,即所写代码只能应用在特定的域上;(7) 支持众多操作符:=,==,~,!,&&,||;varnish命令行工具varnishadmvarnishadm [-ttimeout][-S secret_file][-T address:port] [-nname] ]vcl.load:重载vcl.use:使用vcl.show:显示vcl.discard:删除varnishd varnishd [-a address[:port]] [-b host[:port]][-d] [-F] [-f config]-a:指明监听的地址和端口,默认端口为6081;-b:指明后端主机和端口;-d:打开debug模式;-F:运行于前台;-f:指明配置文件;-l:指明用于保存日志文件的内存空间大小;-stype[,options]:指明使用的存储后端;varnishtop内存日志区域查看工具-I regex:仅显示被模式匹配到的项目;varnish支持的后端存储机制-s typemalloc[,size]--在内存中保存缓存file[,path[,size[,granularity]]]--在磁盘中使用单个文件保存所有缓存persistent,path,size--持久保存,但目前仍处于测试阶段注意:varnish进程一旦重启,前两种存储机制保存的所有缓存对象将被一律清空;对缓存来讲,预热十分重要,所谓预热即找出热点数据,保存在缓存中;varnish配置1、启动varnish前要先定义其脚本配置文件配置打开资源限制段NFILES=131072多能打开的最大文件数,varnish会自动调整该值;MEMLOCK=82000所能使用的内存空间,varnish会自动调整该值;NPROCS="unlimited"单个用户所能运行的最大线程数;RELOAD_VCL=1定义varnish是否会自动重新装载其缓存策略配置文件;“=1”表示在我们使用脚本重启varnish时,其会自动重载vcl配置文件;配置工作特性段Alternative 1, Minimal configuration, no VCL最小化配置,无缓存策略Alternative 2, Configuration with VCL使用vcl实现后续缓存配置Alternative 3, Advanced configuration高级配置,定义各种参数,默认使用此种配置方法Alternative 4, Do It Yourself. See varnishd(1) formore information.有如上4种可用配置选择,使用其中一种进行配置即可采用Alternative 3的详细配置选项VARNISH_VCL_CONF=/etc/varnish/default.vcl缓存策略的默认读取文件;VARNISH_LISTEN_PORT=80因为varnish首先是作为web服务器的反向代理工作的,所以一般应监听于80端口;VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1VARNISH_ADMIN_LISTEN_PORT=6082client interface联系varnish的配置接口,一般此配置接口不应该允许远程打开,所以监听地址为本机;VARNISH_SECRET_FILE=/etc/varnish/secret使用client interface接口联系varnish时使用的密钥文件;VARNISH_MAX_THREADS=3000启用的最大线程数,因为varnish的并发能力有限,所以一般不要超过5000,否则可能导致不稳定;#VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}"VARNISH_STORAGE="malloc,64M"缓存默认保存在磁盘文件中,为了提高性能可修改为保存在内存中;2、定义单个后端主机# vim/etc/varnish/default.vclbackenddefault {.host = "172.16.14.2";.port = "80";}编辑修改完vcl后必须重载配置文件才能生效,但是不建议重启varnish(会导致所有缓存清空),可使用如下方法实现:#varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082varnish> vcl.list //查看已有的vcl200 active 2 bootvarnish> vcl.loadtest1 ./default.vcl//编译刚刚修改的vcl200 VCL compiled.varnish> vcl.list//显示刚修改的vcl已可使用200 active 2 bootavailable 0 test1varnish> vcl.use test1//重载修改过的vcl200 varnish> vcl.list//显示重载成功,已在使用200 available 2 bootactive 0 test13、定义多个后端主机backend NAME {.host=.port=}vcl中条件判断的格式单分支:if(CONDITION) {...;} 双分支:if(CONDITION) {...;}else {...;}多分支:if(CONDITION1) {...;}elseif (CONDITION2) {...;}else {...;}示例:在响应报文首部添加新首部以显示缓存的命中情况sub vcl_deliver {if(obj.hits>0){setresp.http.X-Cache = "HIT";}else {set resp.http.X-Cache= "MISS";}}网页测试结果vcl中常用变量1、任何阶段(引擎)都可用的变量(图示任何阶段)now:获取当前时间;.host:后端主机IP地址或主机名;.port:后端主机端口号或服务名;2、处理请求阶段可用的变量(图示1)client.ip:客户端ip;server.hostname:varnish主机名;server.ip:varnish ip;server.port:varnish端口号;req.request:请求方法;req.url:请求的url;req.proto:请求的http协议版本;req.backend:用于服务此次请求的后端主机;req.backend.healthy:后端主机的健康状态;req.http.HEADER:引用请求报文中指定的首部;req.can_gzip:客户端是否能够接受gzip压缩格式的响应内容;req.restarts:此请求被重启的次数;3、varnish向backend主机发起请求前可用的变量(图示2)bereq.request:请求方法;bereq.url:bereq.proto:bereq.http.HEADERbereq.connect_timeout: 等待与backend建立连接的超时时长;4、backend响应报文到达varnish后,放置于cache中之前可用的变量(图示3之后4之前)beresp.do_stream:流式响应;beresp.do_gzip:是否压缩之后再存入缓存;beresp.do_gunzip:是否解压后再存入缓存;beresp.http.HEADER:beresp.proto:beresp.status:响应状态码;beresp.response:响应时的原因短语;beresp.ttl:响应对象剩余的生存时长,单位为second,此值可自定义修改;beresp.backend.name:此响应报文来源backend名称;beresp.backend.ipberesp..backend.portberesp.storage:将响应报文存储在指定的存储后端5、缓存对象存入cache之后可用的变量(图示4)obj.protoobj.status:响应状态码;obj.response:响应时的原因短语;obj.ttlobj.hits:缓存对象命中次数;obj.http.HEADER6、在决定对请求键作hash计算时可用的变量req.hash:以指定查询缓存的键做为hash计算的键;7、在为客户端构建响应报文时可用的变量(图示5)resp.protoresp.statusresp.responseresp.http.HEADER变量适用位置及相应权限表Varnish检测后端主机的健康状态Varnish可以检测后端主机的健康状态,在判定后端主机失效时能自动将其从可用后端主机列表中移除,而一旦其重新变得可用还可以自动将其设定为可用。为了避免误判,Varnish在探测后端主机的健康状态发生转变时(比如某次探测时某后端主机突然成为不可用状态),通常需要连续执行几次探测均为新状态才将其标记为转换后的状态。每个后端服务器当前探测的健康状态探测方法通过.probe进行设定,其结果可由req.backend.healthy变量获取,也可通过varnishlog中的Backend_health查看或varnishadm的debug.health查看。backendweb1 {.host = "www.zrcj.com";.probe= {.url= "/.healthtest.html";.interval= 1s;.window= 5;.threshold= 2;}}.probe中的探测指令常用的有:(1).url:探测后端主机健康状态时请求的URL,默认为“/”;(2).request:探测后端主机健康状态时所请求内容的详细格式,定义后,它会替换.url指定的探测方式;比如:.request="GET /.healthtest.html HTTP/1.1"//使用1.1版本“GET”方法请求健康状态检测页面"Host: www.zrcj.com" //请求的主机名"Connection: close";//不使用“keepalive”长连接(3).window:设定采样范围,在判定后端主机健康状态时基于最近多少次的探测进行,默认是8;(4).threshold:设定阈值,在.window中指定的次数中,至少有多少次是成功的才判定后端主机正健康运行;默认是3;(5).initial:Varnish启动时对后端主机至少需要多少次的成功探测,默认同.threshold;(6).expected_response:期望后端主机响应的状态码,一般与“.url”或“.request”结合使用,默认为200;(7).interval:探测请求的发送周期,默认为5秒;(8).timeout:每次探测请求的过期时长,默认为2秒;Varnish调度多台后端主机Varnish中可以使用director指令将一个或多个近似的后端主机定义为一个逻辑组,并能以指定的调度方式来轮流将请求发送至这些主机上。不同的director可以使用同一个后端主机,而某director也可以使用“匿名”后端主机(在director中直接进行定义)。每个director都必须有其专用名,且在定义后必须在VCL中进行调用,VCL中任何可以指定后端主机的位置均可以按需将其替换为调用某已定义的director。定义格式backendweb1 {.host= "backweb1.magedu.com";.port= "80";}directorwebservers random {.retries= 5;{.backend= web1;.weight= 2;}{.backend= {.host= "backweb2.magedu.com";.port= "80";}.weight= 3;}}如上示例中,web1为显式定义的后端主机,而webservers这个director还包含了一个“匿名”后端主机(backweb2.magedu.com)。webservers从这两个后端主机中挑选一个主机的方法为random,即以随机方式挑选。director调度算法Varnish的director支持的挑选方法中比较简单的有round-robin和random两种。其中,round-robin类型没有任何参数,只需要为其指定各后端主机即可,挑选方式为“轮叫”,并在某后端主机故障时不再将其视作挑选对象;random方法随机从可用后端主机中进行挑选,每一个后端主机都需要一个.weight参数以指定其权重,同时还可以director级别使用.retries参数来设定查找一个健康后端主机时的尝试次数。Varnish2.1.0后,random挑选方法又多了两种变化形式client和hash。client类型的director使用client.identity作为挑选因子,这意味着client.identity相同的请求都将被发送至同一个后端主机。client.identity默认为client.ip,但也可以在VCL中将其修改为所需要的标识符。类似地,hash类型的director使用hash数据作为挑选因子,这意味着对同一个URL的请求将被发往同一个后端主机,其常用于多级缓存的场景中。然而,无论是client还hash,当其倾向于使用后端主机不可用时将会重新挑选新的后端其机。fallback定义备用服务器directorb3 fallback {{ .backend = www1; }{ .backend = www2; } //will only be used if www1 is unhealthy.{ .backend = www3; } //will only be used if both www1 and www2} // are unhealthy.范文示例HTTP/1.1“Cache-Control”首部示例Cache-Control = "Cache-Control" ":"1#cache-directive cache-directive = cache-request-directive | cache-response-directive cache-request-directive = "no-cache" | "no-store" (backup) | "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默认常规设置 sub vcl_recv {1、设置请求报文首部,使后端主机的日志能记录请求的真正客户端来源 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; } }2、设置拒绝指定来源的主机访问 if (client.ip =="172.16.250.133") { error 404 "Not Found"; }3、设置无法识别的请求方法直接送给后端主机处理 if (req.request != "GET"&& req.request != "HEAD"&& req.request != "PUT"&& req.request != "POST"&& req.request != "TRACE"&& req.request != "OPTIONS"&& req.request != "DELETE") { /* Non-RFC2616 or CONNECT which isweird. */ return (pipe); }4、设置可以创建缓存的请求方法 if (req.request != "GET"&& req.request != "HEAD") { /* We only deal with GET and HEAD bydefault */ return (pass); } if (req.http.Authorization ||req.http.Cookie) { /* Not cacheable by default */ return (pass); } return (lookup); }使“设置1”生效的方法:修改后端主机的http日志记录格式#vim /etc/httpd/conf/httpd.confLogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\"\"%{User-Agent}i\"" combined查看后端主机日志记录#tail /var/log/httpd/access_log显示修改已然生效设置以指定主机响应指定访问subvcl_recv {if (req.http.host ~ "(?i)^(www.)?magedu.com$") { set req.http.host = "www.magedu.com"; set req.backend = www;//若访问www.magedu.com,以www主机响应} elsif (req.http.host ~"(?i)^images.magedu.com$") { set req.backend = images;//若访问images.magedu.com,以images主机响应} else { error 404 "Unknown virtual host";//非以上两种访问请求,响应404错误}}移除单个缓存对象purge用于清理缓存中的某特定对象及其变种(variants),因此,在有着明确要修剪的缓存对象时可以使用此种方式。HTTP协议的PURGE方法可以实现purge功能,不过,其仅能用于vcl_hit和vcl_miss中,它会释放内存工作并移除指定缓存对象的所有Vary:-变种,并等待下一个针对此内容的客户端请求到达时刷新此内容。另外,其一般要与return(restart)一起使用。下面是个在VCL中配置的示例。aclpurgers {"127.0.0.1";"172.16.0.0"/16;}//指明允许发起“purge”操作的主机subvcl_recv {if(req.request == "PURGE") {if(!client.ip ~ purgers) {error405 "Method not allowed";}return(lookup);}}//非允许的主机发起的“purge”请求被返回错误提示,允许的继续查缓存subvcl_hit {if(req.request == "PURGE") {purge;error200 "Purged";}}//缓存名字,则删除此缓存,并返回“成功,已删除”;subvcl_miss {if(req.request == "PURGE") {purge;error404 "Not in cache";}}//缓存未命中,则返回“404错误,无此缓存”subvcl_pass {if(req.request == "PURGE") {error502 "PURGE on a passed object";}}//“purge”发错engine,则返回“502错误,发送错误”
客户端在发起HTTP请求时,只需要为所请求的URL使用PURGE方法即可,其命令使用方式如下:# curl -I -X PURGE http://varniship/path/to/someurl注意:若要使用此设置,必须在可接受的请求方法和可缓存的请求方法中加入“PURGE”方法;设置健康状态检测机制probechk {.url= "/index.html";.window= 5;.threshold= 3;.interval= 3s;.timeout= 1s;}backendappserv {.host= "172.16.14.2";.port= "80";.probe= chk;}backendstatic {.host= "172.16.14.3";.port= "80";.probe= chk;}两后端主机起始状态移除appserv的/index.html文件后移回appserv的/index.html文件后
页:
[1]