一、Varnish简介
二、Varnish特点
三、Varnish系统结构
四、Varnish日志
五、Varnish的算法
六、VCL介绍
七、Varnish后端存储
八、VCL内置函数
九、VCL内置公用变量
十、Http与缓存相关的常用首部
一、Varnish简介
Varnish是一款高性能且开源的反向代理服务器和HTTP缓存加速器;它 的作者Poul-Henning Kamp是FreeBSD的内核开发者之一,他认为现在的计算机比起1975年已经复杂许多。在1975年时,储存媒介只有两种:内存与硬盘。但现在计算机系统的内存除了主存外,还包括了CPU内的L1、L2,甚至有L3快取。硬盘上也有自己的快取装置,因此Squid Cache自行处理物件替换的架构不可能得知这些情况而做到最佳化,但操作系统可以得知这些情况,所以这部份的工作应该交给操作系统处理,这就是 Varnish cache设计架构。
二、Varnish特点
1、是基于内存缓存,重启后数据将消失 |
2、利用虚拟内存方式,io性能好 |
3、支持设置0~60秒内的精确缓存时间 |
4、VCL配置管理比较灵活 |
5、32位机器上缓存文件大小为最大2G |
6、具有强大的管理功能,例如top,stat,admin,list等 |
7、状态机设计巧妙,结构清晰 |
8、利用二叉堆管理缓存文件,达到积极删除目的 |
三、Varnish系统结构
Varnish主要有两个进程:Management与Child进程(也称为Cache进程);如下图:
Management进程主要实现了应用新更新的配置,编译VCL配置、监控Varnish、初始化Varnish及提供一个管理接口等;Management进程会每隔一段时间检测一下Child进程是否运行正常,如果在指定的时间内没有响应则Memagement会重启Child进程
四、Varnish日志
为了与系统的其它部分进行交互,Child进程使用了可以通过文件系统接口进行访问的共享内存日志(shared memory log),因此,如果某线程需要记录信息,其仅需要持有一个锁,而后向共享内存中的某内存区域写入数据,再释放持有的锁即可。而为了减少竞争,每个worker线程都使用了日志数据缓存。共享内存日志大小一般为90M,其分为两部分,前一部分为计数器,后半部分为客户端请求的数据。varnish提供了多个不同的工具如varnishlog、varnishncsa或varnishstat等来分析共享内存日志中的信息并能够以指定的方式进行显示
五、Varnish的算法
Varnish的director支持的挑选方法中比较简单的有round-robin和random两种。其中,round-robin类型没有任何参数,只需要为其指定各后端主机即可,挑选方式为“轮叫”,并在某后端主机故障时不再将其视作挑选对象;random方法随机从可用后端主机中进行挑选,每一个后端主机都需要一个.weight参数以指定其权重,同时还可以director级别使用.retires参数来设定查找一个健康后端主机时的尝试次数。
2.1.0后,random挑选方法又多了两种变化形式client和hash。client类型的director使用client.identity作为挑选因子,这意味着client.identity相同的请求都将被发送至同一个后端主机。client.identity默认为cliet.ip,但也可以在VCL中将其修改为所需要的标识符。类似地,hash类型的director使用hash数据作为挑选因子,这意味着对同一个URL的请求将被发往同一个后端主机,其常用于多级缓存的场景中。然而,无论是client还hash,当其倾向于使用后端主机不可用时将会重新挑选新的后端其机。
六、VCL介绍
Varnish Configuration Language (VCL)是varnish配置缓存策略的工具,它是一种基于“域”(domain specific)的简单编程语言,可以使用指定运算符:“=”,比较运算符“==”,逻辑运算符“!,&&,!!”等;支持使用正则表达式进行字符串匹配、允许用户使用set自定义变量、支持if判断语句,也有内置的函数和变量等;VCL策略在启用前,会由management进程将其转换为C代码,而后再由gcc编译器将C代码编译成二进制程序。编译完成后,management负责将其连接至varnish实例,即child进程
七、Varnish后端存储
varnish支持多种不同类型的后端存储,这可以在varnishd启动时使用-s选项指定。后端存储的类型包括:
file:使用特定的文件存储全部的缓存数据,并通过操作系统的mmap()系统调用将整个缓存文件映射至内存区域(如果条件允许) |
malloc:使用malloc()库调用在varnish启动时向操作系统申请指定大小的内存空间以存储缓存对象 |
persistent(experimental):与file的功能相同,但可以持久存储数据(即重启varnish数据时不会被清除);仍处于测试期 |
varnish无法追踪某缓存对象是否存入了缓存文件,从而也就无从得知磁盘上的缓存文件是否可用,因此,file存储方法在varnish停止或重启时会清除数据。而persistent方法的出现对此有了一个弥补,但persistent仍处于测试阶段,例如目前尚无法有效处理要缓存对象总体大小超出缓存空间的情况,所以,其仅适用于有着巨大缓存空间的场景。
选择使用合适的存储方式有助于提升系统性,从经验的角度来看,建议在内存空间足以存储所有的缓存对象时使用malloc的方法,反之,file存储将有着更好的性能的表现。然而,需要注意的是,varnishd实际上使用的空间比使用-s选项指定的缓存空间更大,一般说来,其需要为每个缓存对象多使用差不多1K左右的存储空间,这意味着,对于100万个缓存对象的场景来说,其使用的缓存空间将超出指定大小1G左右。另外,为了保存数据结构等,varnish自身也会占去不小的内存空间
为varnishd指定使用的缓存类型时,-s 选项可接受的参数格式如下: |
malloc[,size] 或 file[,path[,size[,granularity]]] 或 persistent,path,size {experimental} |
八、VCL内置函数
1、vcl_recv函数用于接收和处理请求,当请求到达并成功接收后被调用,通过判断请求的数据来决定如何处理请求。此函数一般以如下几个关键字结束: pass:表示进入pass模式,把请求控制权交给vcl_pass函数。 pipe:表示进入pipe模式,把请求控制权交给vcl_pipe函数。 error code [reason]:表示返回“code”给客户端,并放弃处理该请求,“code”是错误标识,例如200、405等,“reason”是错误提示信息。2、vcl_pipe函数此函数在进入pipe模式时被调用,用于将请求直接传递至后端主机,在请求和返回的内容没有改变的情况下,将不变的内容返回给客户端,直到这个链接关闭此函数一般以如下几个关键字结束: error code [reason] pipe3、vcl_pass函数此函数在进入pass模式时被调用,用于将请求直接传递至后端主机,后端主机应答数据后送给客户端,但不进行任何缓存,在当前连接下每次都返回最新的内容此函数一般以如下几个关键字结束: error code [reason] pass4、lookup表示在缓存里查找被请求的对象,并且根据查找的结果把控制权交给函数vcl_hit或者函数vcl_miss5、vcl_hit函数在执行lookup指令后,如果在缓存中找到请求的内容,将自动调用该函数此函数一般以如下几个关键字结束: deliver:表示将找到的内容发送给客户端,并把控制权交给函数vcl_deliver error code [reason] pass6、vcl_miss函数在执行lookup指令后,如果没有在缓存中找到请求的内容时自动调用该方法,此函数可以用于判断是否需要从后端服务器取内容此函数一般以如下几个关键字结束: fetch:表示从后端获取请求的内容,并把控制权交给vcl_fetch函数 error code [reason] pass7、vcl_fetch函数在从后端主机更新缓存并且获取内容后调用该方法,接着,通过判断获取的内容来决定是否将内容放入缓存,还是直接返回给客户端此函数一般以如下几个关键字结束: error code [reason] pass deliver8、vcl_deliver函数在缓存中找到请求的内容后,发送给客户端前调用此方法。此函数一般以如下几个关键字结束: error code [reason] deliver9、vcl_timeout 函数此函数在缓存内容到期前调用。一般以如下几个关键字结束: discard:表示从缓存中清除该内容。 fetch10、vcl_discard函数在缓存内容到期后或缓存空间不够时,自动调用该方法,一般以如下几个关键字结束: keep:表示将内容继续保留在缓存中 discard
下面以一张VCL处理流程图来VCL函数介绍,对以上VCL函数有一个了解之后,再来看每个函数之间是如何关联的:
处理过程大致分为如下几个步骤:
Receive 状态: | 也就是请求处理的入口状态,根据VCL规则判断该请求应该是Pass或Pipe, 或者进入Lookup(本地查询) |
Lookup 状态: | 进入此状态后,会在hash表中查找数据,若找到,则进入Hit状态,否则进入miss状态 |
Pass 状态: | 在此状态下,会进入后端请求,即进入fetch状态 |
Fetch 状态: | 在Fetch状态下,对请求进行后端的获取,发送请求,获得数据,并进行本地的存储 |
Deliver 状态: | 将获取到的数据发送给客户端,然后完成本次请求 |
九、VCL内置公用变量
VCL内置的公用变量可以用在不同的VCL函数中使用,根据这些公用变量使用的不同阶段,如下介绍:
当请求到达后,可以使用的公用变量如下所示: | |
公用变量名称 | 含义 |
req.backend | 指定对应的后端主机 |
server.ip | 表示服务器端IP |
client.ip | 表示客户端IP |
req.request | 指定请求的类型,例如GET、HEAD、POST等 |
req.url | 指定请求的地址 |
req.proto | 表示客户端发起请求的HTTP协议版本 |
req.http.header | 表示对应请求中的http头部信息 |
req. restarts | 表示请求重启的次数,默认最大值为4 |
Varnish 在向后端主机请求时,可以使用的公用变量如下所示: | |
公用变量名称 | 含义 |
beresp.request | 指定请求的类型,例如GET、HEAD等 |
beresp.url | 指定请求的地址 |
beresp .proto | 表示客户端发起请求的HTTP协议版本 |
beresp .http.header | 表示对应请求中的http头部信息 |
beresp .ttl | 表示缓存的生存周期,也就是cache保留多长时间,单位是秒 |
从cache或者后端主机获取内容后,可以使用的公用变量如下所示: | |
公用变量名称 | 含义 |
obj.status | 返回内容的请求状态代码,例如200、302、504等 |
obj.cacheable | 返回的内容是否可以缓存,也就是说,如果HTTP返回 是200、203、300、301、302、404、410等,并且有非0的生存期,则可以缓存 |
obj.valid | 表示是否是有效的HTTP应答 |
obj.response | 返回内容的请求状态信息 |
obj.proto | 返回内容的HTTP协议版本 |
obj.ttl | 返回内容的生存周期,也就是缓存时间,单位是秒 |
obj.lastuse | 返回上一次请求到现在的间隔时间,单位是秒 |
对客户端应答时,可以使用的公用变量如下所示: | |
公用变量名称 | 含义 |
resp.status | 返回客户端的HTTP状态代码 |
resp.proto | 返回客户端的HTTP协议版本 |
resp.http.header | 返回客户端的HTTP头部信息 |
resp.response | 返回客户端的HTTP状态信息 |
上面主要介绍了一些常用的VCL内置公用变量,如果想要了解更多请查阅Varnish官方文档
如果想要精确熟练的配置缓存,需要对Http协议有个详细了解,这里只介绍一些常用的与缓存相关的Http首部
十、Http与缓存相关的常用首部
Expires:指定WEB对象的过期时间,不宜设置缓存时间过长
Cache-Control:定义所有缓存机制必须遵循的缓存指示,包括如下一些指令:
max-age=[seconds]:执行缓存被认为是最新的最长时间。类似于过期时间,这个参数是基于请求时间的相对时间间隔,而不是绝对过期时间
s-maxage=[seconds]:类似于max-age属性,除了他应用于共享(如:代理服务器)缓存
public:响应被缓存,并且在多用户之间共享
no-cache:强制每次请求直接发送给源服务器,而不经过本地缓存
no-store:绝对禁止缓存,用于一些敏感数据
Must-revalidate:保证缓存的数据都是最新的,会到服务器端验证当前数据是不是最新的数据
proxy-revalidate:和must-revalidate类似,除了他只对缓存代理服务器起作用
Etag:响应首部,是实体标签(EntityTag)的缩写,用于在响应报文中为某web资源定义版本标识符
Last-Mofified:响应首部,用于回应客户端关于Last-Modified-Since或If-None-Match首部的请求,以通知客户端其请求的web对象最近的修改时间
If-Modified-Since:条件式请求首部,如果在此首部指定的时间后其请求的web内容发生了更改,则服务器响应更改后的内容,否则就响应304状态码(not modified);
If-None-Match:条件式请求首部;web服务器为某web内容定义了Etag首部,客户端请求时能获取并保存这个首部的值(即标签);而后在后续的请求中会通过If-None-Match首部附加其认可的标签列表并让服务器端检验其原始内容是否有可以与此列表中的某标签匹配的标签;如果有,则响应304,否则,则返回原始内容
Vary:响应首部,原始服务器根据请求来源的不同响应的可能会有所不同的首部,最常用的是Vary: Accept-Encoding,用于通知缓存机制其内容看起来可能不同于用户请求时Accept-Encoding-header首部标识的编码格式
Age:缓存服务器可以发送的一个额外的响应首部,用于指定响应的有效期限;浏览器通常根据此首部决定内容的缓存时长;如果响应报文首部还使用了max-age指令,那么缓存的有效时长为“max-age减去Age”的结果
下面以实际请求与响应报文来介绍:
######打开百度的请求报文信息----------------------------------------------------------------------Request URL:http://www.baidu.com/ #请求的URLRequest Method:GET #请求方法Status Code:200 OK #状态码Request Headers view source Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 #通知服务器可以发送的类型Accept-Encoding:gzip,deflate,sdch #通知服务器可以发送的编码方式Accept-Language:zh-CN,zh;q=0.8 #通知服务器可以发送的语言Cache-Control:max-age=0 #报文传送缓存指示Connection:keep-alive #保持长连接状态Cookie:BAIDUID=6CB063786195FA7025F715DDACDB9D32:FG=1; BDSVRTM=5; H_PS_PSSID=3524_2777_1464_3421_2975_3090_3501_3439 #Cookie,服务器存储在客户端的信息Host:www.baidu.com #主机地址Referer:http://baidu.com/ User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36 #将发起请求的应和程序名称告知服务器,也就是浏览器
######打开百度的响应报文信息----------------------------------------------------------------------Response Headers view sourceBDPAGETYPE:1 BDQID:0xf06adb8c076aa3faBDUSERID:0Cache-Control:private #缓存指示Connection:Keep-Alive #状态为保持连接Content-Encoding:gzip #对主体执行的编码方式为gzipContent-Length:4386 #响应主体内容的长度为4386个字节Content-Type:text/html;charset=utf-8 #响应类型为HTML文本,编码类型为utf-8Date:Sun, 06 Oct 2013 03:35:47 GMT #响应的时间日期Expires:Sun, 06 Oct 2013 03:35:47 GMT #实体不在有效,要从原始的源端再次获取此实体的日期和时间Server:BWS/1.0 #服务器应用程序软件的名称和版本 BWS/1.0Set-Cookie:BDSVRTM=2; path=/ #设置cookie,pathSet-Cookie:H_PS_PSSID=3524_2777_1464_3421_2975_3090_3501_3439; path=/; domain=.baidu.com #设置cookie,path,domain都是cookie的信息(作用范围等等)
到此,Varnish的基本原理已经讲清楚了,下一篇博客将会写Varnish的安装配置;敬请关注...