码迷,mamicode.com
首页 > 其他好文 > 详细

varnish详解

时间:2015-06-06 22:12:55      阅读:346      评论:0      收藏:0      [点我收藏+]

标签:缓存   cache   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-Controlmax-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]

param.set[param] [value]

Log file

varnish的日志,为保持其高性能,保持在一段共享内存空间中--Shared Memory Log,共享内存日志大小一般为90M,分为两部分:前一部分为计数器,后一部分为客户请求相关的数据,可以计算缓存命中率、分析请求首部、记录响应首部等;

vcl:Varnish Configuration Language

缓存策略配置工具;

基于“域”的简单编程语言,意为其所有代码都是写在各“{}”中,且作用范围仅为对应的“{}”,这些“域”称为varnish的状态引擎;

varnish定义其最大并发连接数:线程池模型:

thread_pools:线程池个数;默认为2;

thread_pool_max:单线程池内允许启动的最多线程个数;

thread_pool_min

thread_pool_timeout:多于thread_pool_min的线程空闲此参数指定的时长后即被purge;

vcl处理流程图

        技术分享

        技术分享


state engine

vcl配置的缓存策略在这些stateengine上发挥作用;state engine之间有相关性,上级engine通过return指明下级engine;varnish处理请求前要先分析该请求的http首部;

vcl_init:

在装载vcl,用其处理任何请求之前;

vcl_recv

请求被接入,但在其被分析、处理完成之前;

是否响应此请求、如何响应、使用哪个后端主机响应;

vcl_pipe

vcl_hash

vcl_hit

vcl_miss

vcl_pass

vcl_fetch

从backend主机收到响应报文之前被调用;可return的值:deliver、errorcode [reason]、restart;

vcl_deliver

vcl_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命令行工具

varnishadm

varnishadm [-t  timeout]  [-S secret_file]  [-T address:port] [-nname] [command [...]]

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:指明用于保存日志文件的内存空间大小;

-s[name=]type[,options]:指明使用的存储后端;

varnishtop

内存日志区域查看工具

-I regex:仅显示被模式匹配到的项目;

varnish支持的后端存储机制

-s type

malloc[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.1

VARNISH_ADMIN_LISTEN_PORT=6082

client 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.vcl

backenddefault {

  .host = "172.16.14.2";

  .port = "80";

}

编辑修改完vcl后必须重载配置文件才能生效,但是不建议重启varnish(会导致所有缓存清空),可使用如下方法实现:

#varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082

varnish> vcl.list       //查看已有的vcl

200       

active         2 boot

varnish> vcl.loadtest1 ./default.vcl//编译刚刚修改的vcl

200       

VCL compiled.

varnish> vcl.list//显示刚修改的vcl已可使用

200       

active         2 boot

available      0 test1

varnish> vcl.use test1//重载修改过的vcl

200       

varnish> vcl.list//显示重载成功,已在使用

200       

available      2 boot

active         0 test1

3、定义多个后端主机

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:此请求被重启的次数;

3varnishbackend主机发起请求前可用的变量(图示2

bereq.request:请求方法;

bereq.url:

bereq.proto:

bereq.http.HEADER

bereq.connect_timeout: 等待与backend建立连接的超时时长;

4backend响应报文到达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.ip

beresp..backend.port

beresp.storage:将响应报文存储在指定的存储后端

5、缓存对象存入cache之后可用的变量(图示4

obj.proto

obj.status:响应状态码;

obj.response:响应时的原因短语;obj.ttl

obj.hits:缓存对象命中次数;

obj.http.HEADER

6、在决定对请求键作hash计算时可用的变量

req.hash:以指定查询缓存的键做为hash计算的键;

7、在为客户端构建响应报文时可用的变量(图示5

resp.proto

resp.status

resp.response

resp.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.conf

LogFormat "%{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文件后

技术分享

varnish详解

标签:缓存   cache   varnish   

原文地址:http://10066605.blog.51cto.com/10056605/1659272

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!