企业级缓存系统varnish应用

场景

随着公司业务快速发展,公司的电子商务平台已经聚集了很多的忠实粉丝,公司也拿到了投资,这时老板想通过一场类似双十一的活动,进行一场大的促销,届时会有非常多的粉丝访问网站,你的总监与市场部门开完会后,确定活动期间会有平常10倍以上的访问请求,总监要求大幅增加网站容量,除了去扩容服务器之外,还有没有其他办法呢?

技术说明

如今,互联网应用规模不断扩大,应用不断增多,网络已经深入到我们生活的各个方面,给我们的日常生活带来极大方便,随着信息系统的业务扩展,网站用户增加,历史数据不断积累,业务不断增长,系统的响应速度、请求的处理能力开始下降,CPU和磁盘IO处理能力会成为瓶颈。

如果在有限的硬件投入前提下,提高系统性能就成了主要解决问题手段,为了提供高性能的web服务,可采用包括负载均衡、页面静态化、数据库集群、缓存等多种技术方案,其中,缓存方案是目前主流的性能优化方式,对于网站来说,有很多特定信息系统的请求都是重复冗余的,我们的系统经常在做重复的计算和传输着相同的内容。这也就意味着在有限的计算资源条件下,可通过缓存技术,大幅提高请求处理能力以及处理速度。

企业服务架构图

缓存介绍

缓存,又称加速器,用于加速运行速度较快的设备与较慢设备之间的通信。基于程序的运行具有局部性特征其能实现加速的功能:

缓存有效与否,是通过缓存命中率来衡量的。缓存命中,意味着在请求某资源时,在缓存中找到该资源,并响应给客户端。

缓存命中率的计算方式:hits/(hits+misses) (0-1)(命中的/命中的+没有命中的)

缓存是把之前访问到的数据及其周边的数据放置于具有更快速度的、效率更高的设备中来完成加速。使用缓存之后, 资源的请求与响应过程由client–>server–>client改变为client–>cache[–>server]–>client。若在缓存中没有查找到相应的资源,将会由缓存向server请求该资源,这一过程,会造成额外的开销,若换成命中率过低,则会造成资源的浪费。因此,提高缓存的命中率是必然的。

 

缓存系统的关键点

缓存之所以能够生效是程序的运行具有局部性特征:

时间局部性:一个数据被访问过之后,可能很快会被再次访问到;

空间局部性:一个数据被访问时,其周边的数据也有可能被访问到(比如:nginx定义一个缓存,首先要定义一个缓存路径和空间大小,当缓存空间满了,就会使用lru算法(最近最少使用算法),将最近最少访问的缓存数据给清理掉,那么剩下的就是访问最多的数据,也就是热点数据)

时效性:

缓存空间耗尽:LRU,最近最少使用;(Least recently used,最近最少使用)

缓存过期:到了缓存时间后失效(失效之后不一定会被删除,要看你怎样定义)

缓存命中率:hit/(hit+miss)

缓存的数据类型:

资源缓存:page cache ,static cache;

数据缓存:data cache;

 

浏览器的缓存机制

 

 

浏览器缓存控制机制有两种:HTML Meta标签 vs. HTTP头信息

1、HTML Meta标签

浏览器缓存机制,其实主要就是HTTP协议定义的缓存机制(如: Expires;Cache-control等)。但是也有非HTTP协议定义的缓存机制,如使用HTMLMeta 标签,Web开发者可以在HTML页面的<head>节点中加入<meta>标签,代码如下:

<META HTTP-EQUIV="Pragma" CONTENT="no-cache">

 

2、HTTP头信息

 

1.0版本的Expires策略:Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。不过Expires 是HTTP 1.0的东西,现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。Expires 的一个缺点就是,返回的到期时间是服务器端的时间,这样存在一个问题,如果客户端的时间与服务器的时间相差很大(比如时钟不同步,或者跨时区),那么误差就很大,所以在HTTP 1.1版开始,使用Cache-Control: max-age=秒替代。

 

1.1版本的Cache-control策略(重点关注):Cache-Control与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires。

 

1.值可以是public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age

请求报文的头部(比较常用的是no-cache和max-age)

响应报文的头部

响应头部的Etag和请求头部的If-None-Match做比对

响应头部的Last-Modified和请求头部的If-Modified-Since做对比

 

2.Last-Modified/If-Modified-Since

Last-Modified/If-Modified-Since要配合Cache-Control使用。

(1) Last-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。

(2) If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。

 

3.Etag/If-None-Match

Etag/If-None-Match也要配合Cache-Control使用。

(1) Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器觉得)。Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。

(2)If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304

 

4.既生Last-Modified何生Etag?

你可能会觉得使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要Etag(实体标识)呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:

(1) Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间

(2)如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存

(3)有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回200或者304。

 

互联网业务优化三大黄金法则

1、把所有的请求尽可能在架构前层阶段交付,尽可能减少后端交互 (push ahead)

2、对于重复类的业务,尽最大程度的去做缓存策略(cache is king )

3、服务能用内存响应的,坚决不用硬盘 (memory is king)

 

Varnish是什么

常见的缓存服务开源解决方案有varnish、nginx、squid、ats等。Varnish是一款高性能的开源HTTP加速器,挪威最大的在线报纸 Verdens Gang 使用3台Varnish代替了原来的12台Squid,性能比以前更好。

 

Varnish 的作者Poul-Henning Kamp是FreeBSD的内核开发者之一,varnish项目是2006年发布的第一个版本0.9.距今已经有十年了,此文档之前也提过varnish还不稳定,那是2007年时候编写的,经过varnish开发团队和网友们的辛苦耕耘,现在的varnish已经很健壮。很多门户网站已经部署了varnish,并且反应都很好,甚至反应比squid还稳定,且效率更高,资源占用更少。相信在反向代理,web加速方面,varnish已经有足够能力代替squid。

 

Varnish的特性

1、Varnish的稳定性很高,两者在完成相同负荷的工作时,Squid服务器发生故障的几率要高于Varnish,因为使用Squid要经常重启;

2.Varnish访问速度更快,因为采用了"Page Cache"技术,所有缓存数据都直接从内存读取(映射),而squid是从硬盘读取,因而Varnish在访问速度方面会更快;

3、Varnish可以支持更多的并发连接,因为Varnish的TCP连接释放要比Squid快,因而在高并发连接情况下可以支持更多TCP连接;

4、Varnish可以通过管理端口,使用正则表达式批量的清除部分缓存,而Squid是做不到的;

5、squid属于是单进程使用单核CPU,但Varnish是通过fork形式打开多进程来做处理,所以可以合理的使用所有核来处理相应的请求

 

Varnish的架构

varnish主要运行两个进程:Management进程和Child进程(也叫Cache进程)。

 

Management进程主要实现应用新的配置、编译VCL、监控varnish、初始化varnish以及提供一个命令行接口等。Management进程会每隔几秒钟探测一下Child进程以判断其是否正常运行,如果在指定的时长内未得到Child进程的回应,Management将会重启此Child进程。

 

Child进程包含多种类型的线程,Varnish依赖"工作区(workspace)"以降低线程在申请或修改内存时出现竞争的可能性。在varnish内部有多种不同的工作区,其中最关键的当属用于管理会话数据的session工作区。

 

Acceptor线程:接收新的连接请求并响应;

Worker线程:child进程会为每个会话启动一个worker线程,此worker线程真正来管理缓存,构建响应报文,因此,在高并发的场景中可能会出现数百个worker线程甚至更多;

Expiry线程:从缓存中清理过期内容;

 

Varnish安装方式

Varnish安装常用两种方式,yum安装和源码包安装

yum 安装:通常是在线安装,好处是安装方式简单,不易出错;常用的安装yum源为epel

源码包安装:是先将 Varnish 的源码下载下来,在自己的系统里编译生成可执行文件,然后执行,好处是因为是在自己的系统上编译的,更符合自己系统的性能,也就是说在自己的系统上执行 Varnish 服务性能效率更好。(推荐)

区别:路径和启动方式不同,支持的模块也不同。

 

Varnish程序路径

主程序:/usr/sbin/varnishd

命令行管理工具程序: /usr/bin/varnishadm

主配置文件:/etc/varnish/default.vcl

性能配置文件:/etc/varnish/varnish.params

Unit file: /usr/lib/systemd/system/varnish.service(centos7)

Init.file :/etc/init.d/varnish (centos6)

 

Varnish配置文件讲解

Varnish的主配置文件路径一般在/etc/varnish/default.vcl下(yum安装)

VCL,Varnish Configuration Language 是varnish配置缓存策略的工具,它是一种基于"域"(可想象与iptables的几个链,也就是类似钩子函数)的简单编程语言,它支持有限的算术运算和逻辑运算操作、允许使用正则表达式进行字符串匹配、允许用户使用set自定义变量、支持if判断语句,也有内置的函数和变量等。

 

使用VCL编写的缓存策略通常保存至.vcl文件中,其需要编译成二进制的格式后才能由varnish调用。事实上,整个缓存策略就是由几个特定的子例程如vcl_recv、vcl_hash等组成,它们分别在不同的位置(或时间)执行,如果没有事先为某个位置自定义子例程,varnish将会执行默认的定义。

 

VCL策略在启用前,会由management进程将其转换为C代码,而后再由gcc编译器将C代码编译成二进制程序。编译完成后,management负责将其连接至varnish实例,即child进程。正是由于编译工作在child进程之外完成,它避免了装载错误格式VCL的风险。因此,varnish修改配置的开销非常小,其可以同时保有几份尚在引用的旧版本配置,也能够让新的配置即刻生效。编译后的旧版本配置通常在varnish重启时才会被丢弃,如果需要手动清理,则可以使用varnishadm的vcl.discard命令完成。

 

varnish程序功能的配置文件介绍

RELOAD_VCL=1

设置为1表示当使用systemctl reload varnish时,会自动重新装载vcl的配置文件,也就是能够让新的配置生效

VARNISH_VCL_CONF=/etc/varnish/default.vcl

加载的缓存策略的配置文件路径

#VARNISH_LISTEN_ADDRESS=

varnish服务监听的地址,默认是监听在本机所有可用的地址上

VARNISH_LISTEN_PORT=6081

#varnish监听的端口,因为varnish要作为web服务器的反代进行工作时,才能将http的内容缓存,一般要将其改为80端口,但是实际生产环境中,varnish一般是处于前端调度器的后面,所以可以在前端调度器上将调度的端口改为此处的端口也可以

VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1

varnish管理接口监听的地址,监听在127.0.0.1表示只允许从本机登录进行管理

VARNISH_ADMIN_LISTEN_PORT=6082

varnish管理接口监听的端口

VARNISH_SECRET_FILE=/etc/varnish/secret

varnish管理时的秘钥文件(用varnish管理内存是一种危险的操作,是需要认证的,而这个认证是基于一个文件内的一串字符,所以要指定认证文件)

VARNISH_STORAGE="file,/var/lib/varnish/varnish_storage.bin,1G"

varnish缓存时,使用哪种存储方式对缓存内容进行存储,本处是指使用file文件方式,存在

/var/lib/varnish/varnish_storage.bin文件中,总共使用1G大小的空间如果要使用内存缓存,则可以定义为:

"malloc,400M"在很多生产环境还是使用file,但是将文件放在固态硬盘,如果希望性能更好点,放在PCI-E的固态硬盘fution-IO

三种缓存方式:

1.malloc,直接在内存上开启一块空间做缓存

2.file,在内存上映射到硬盘上一块空间做缓存

3.persistent通过文件持久存储,persistent,path,size 前两者在重启后缓存都会消失,persistent可以永久保存缓存,但还为开发阶段

VARNISH_TTL=120

如果后端服务器没有指明缓存内容的TTL时间,则varnish自身为缓存定义的TTL时间

VARNISH_USER=varnish

VARNISH_GROUP=varnish

 

 

 

VCL有多个状态引擎,状态之间存在相关性,但状态引擎彼此间互相隔离;每个状态引擎可使用return(x)指明关联至哪个下一级引擎;每个状态引擎对应于vcl文件中的一个配置段

vcl_recv:接受用户请求进varnish的入口的引擎,接受到结果之后,利用return(lookup),将请求转交给vcl_hash引擎进行处理

vcl_hash:接受到用户请求后,对用户请求的URL进行hash计算,根据请求的首部信息,以及hash结果进行下一步处理的引擎

vcl_hit:经过vcl_hash引擎处理后,发现用户请求的资源本地有缓存,则vcl_hash引擎通过return(hit)将请求交给vcl_hit引擎进行处理,vcl_hit引擎处理后将请求交给vcl_deliver引擎,vcl_deliver引擎构建响应报文,响应给用户

vcl_miss:经过vcl_hash引擎处理后,发现用户请求的资源本地没有缓存,则vcl_hash引擎通过return(miss)将请求交给vcl_miss引擎进行处理

vcl_purge:经过vcl_hash引擎处理后,发现请求是对缓存的内容进行修剪时,则通过return(purge)交给vcl_purge引擎进行处理,vcl_purge引擎处理后,利用vcl_synth引擎将处理的结果告知给用户

vcl_pipe:经过vcl_hash引擎处理后,发现用户请求的报文varnish无法理解,则通过return(pipe),将请求交给vcl_pipe引擎,pipe引擎直接将请求交给后端真实服务器

vcl_pass:当请求经过vcl_hash处理后,发现请求报文不让从缓存中进行响应或其他原因没办法查询缓存,则由return(pass)或return(hit-for-pass)交由vcl_pass引擎进行处理

vcl_backend_fetch:当发现缓存未命中或由vcl_pass传递过来的某些不能查询缓存的请求,交由vcl_backend_fetch引擎处理,vcl_backend_fetch引擎会向后端真实web服务器发送请求报文,请求对应的资源

vcl_backend_response:当后端发送响应报文到varnish后,会由vcl_backend_resonse引擎进行处理,如:判断响应的内容是否可缓存,如果能缓存,则缓存下来后,交给vcl_deliver引擎,如果不能缓存,则直接交给vcl_deliver引擎,vcl_deliver引擎构建响应报文给客户端

varnish4.0版本的两个特殊的引擎

vcl_init(初始化,定义服务器集群):在处理任何请求之前要执行的vcl的代码,主要用于初始化VMOD,可用在后端主机有多台时,借助此引擎完成多台主机的负载均衡效果

vcl_fini:所有的请求都已经结束,在vcl配置被丢弃时调用;主要用于清理VMOD(一般不用配)

 

常见的状态引擎之间的处理流程为:

如果缓存命中:

用户请求–>vcl_recv–>vcl_hash–>vcl_hit–>vcl_deliver–>响应给用户

如果缓存未命中:

用户请求–>vcl_recv–>vcl_hash–>vcl_miss–>vcl_backend_fetch–>后端服务器接受请求发送响应报文–>vcl_backend_response–>vcl_deliver

或:

用户请求–>vcl_recv–>vcl_hash–>vcl_miss–>vcl_pass–>vcl_backend_fetch–>后端服务器接受请求发送响应报文–>vcl_backend_response–>vcl_deliver–>响应给用户

如果不能从缓存中进行响应

用户请求–>vcl_recv–>vcl_hash–>vcl_pass–>vcl_backend_fetch–>后端服务器接受请

求发送响应报文–>vcl_backend_response–>vcl_deliver–>响应给用户

 

如果请求报文无法理解

用户请求–>vcl_recv–>vcl_pipe–>交给后端服务器

 

vcl语法格式

<1>配置文件第一个非注释行必须是vcl 4.0,标明此vcl配置文件是基于vcl4.0版本

<2>//、#或/ comment /用于单行或多行注释

<3>sub $NAME 定义函数,子例程

<4>不支持循环,支持条件判断,有内置变量

<5>使用终止语句return(XXX),没有返回值,仅仅是标明下一步交给哪个状态引擎,没有走默认

<6>域专用,语句用{ }括起来,用sub声明,指明为哪一段的专用代码,如:sub vcl_recv{…},可理解为一个配置段

<7> 每个语句必须以;分号结尾

<8> 每个变量有其能使用的引擎的位置,可理解为变量由其可用的配置段

<9>操作符:=(赋值)、==(等值比较)、~(模式匹配)、!(取反)、&&(逻辑与)、||(逻辑或)、>(大于)、>=(大于等于)、<(小于)、<=(小于等于)

 

vcl常见内建的函数

VCL提供了几个函数来实现字符串的修改,添加bans,重启VCL状态引擎以及将控制权转回Varnish等。

ban(expression):清除能被表达式匹配的所有缓存对象

ban_url(regex):清除所有其URL能够由regex匹配的缓存对象;

hash_data(str):对指定的字符串做hash计算后的结果,例如:hash_data(req.url)对请求报文的url进行哈希运算

return(): 当某VCL域运行结束时将控制权返回给Varnish,并指示Varnish如何进行后续的动作;其可以返回的指令包括:lookup、hash、hit、miss、pass、pipe、hit_for_pass、purge等;但某特定域可能仅能返回某些特定的指令,而非前面列出的全部指令;

 

vcl的内建变量的分类

req.*:req.开头的变量,由客户端发来的http请求相关的变量:req.method 表示客户端的请求方法

bereq.* :bereq.开头的变量,varnish主机在向后端真实服务器发送http请求报文时的相关变量

beresp.*:beresp.开头的变量,由后端真实服务器发来的http响应报文中的某些首部信息相关的变量,一般

是在vcl_backend_response或vcl_backend_fenth引擎中调用

resp.*:resp.开头的变量,由varnish响应给客户端的响应报文相关的变量

obj.* :obj.开头的变量,对存储在缓存空间中的缓存对象属性的引用变量。obj开头的变量都是只读的

obj.hits: 某个缓存对象的缓存的命中次数

client.,server.,storage.*:可用在所有面向客户端一侧的引擎中,也就是vcl_recv、vcl_pipe、vcl_hash

、vcl_pass、vcl_purge、vcl_miss、vcl_hit、vcl_deliver、vcl_synth中

用户还可自定义:

set

unset

 

 

 

Varnish配置文件讲解示例

1.实现对登陆页面/login或admin的请求不检查缓存

vcl_recv {

if (req.url ~ "(?i)^/(login|admin)") {

return(pass);

}

}

2.拒绝某种请求访问

vcl_recv {

if (req.http.User-Agent ~ "(?i)curl") {

return(synth(405));

}

}

(?i)即匹配时不区分大小写。

3.对资源去除cookie,并设定缓存时长

if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|css|js)$") {

unset beresp.http.Set-Cookie;

set beresp.ttl = 3600s;

}

4.显示后端主机IP

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;

}

5、配置varnish后端多台主机

import directors; # 导入模块

backend server1 {

.host = "172.16.42.2";

.port = "80";

}

backend server2 {

.host = "172.16.42.3";

.port = "80";

}

6、定义后端健康监测

.probe:定义健康状态检测方法;

.url:检测时请求的URL,默认为"/";

.request:发出的具体请求;

.window:基于最近的多少次检查来判断其健康状态;

.threshhold:最近.window中定义的这么次检查中至有.threshhold定义的次数是成功的;

.interval:检测频度;

.timeout:超时时长;

.expected_response:期望的响应码,默认为200;

backend server1 {

.host = "172.16.42.3";

.port = "80";

.probe = {

.url= "/.healthcheck.html" #得先创建这个测试页面;

.timeout= 1s;

.interval= 2s;

.window=5;

.threshold=5;

}

}

7、varnish动静分离

backend default {

.host = "172.16.42.10";

.port = "80";

}

backend appsrv {

.host = "172.16.42.2";

.port = "80";

}

sub vcl_recv {

if (req.url ~ "(?i)\.php$") {

set req.backend_hint = appsrv;

} else {

set req.backend_hint = default;

}

}

 

Varnish管理工具介绍

登录:

-S /etc/varnish/secret -T 127.0.0.1:80

配置文件相关:

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

 

Varnish常见工具使用

1>、varnishstat – Varnish Cache statistics 各种计数器

-1 批次显示,只显示1次

-1 -f FILED_NAME

-l:可用于-f选项指定的字段名称列表;

# 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>

varnishtop -i RespStatus 查看响应码

3>、varnishlog – Display Varnish logs 查看实时日志

4>、 varnishncsa – Display Varnish logs in Apache / NCSA combined log format 标准日

志格式

 

Varnish进行缓存清理

将缓存删除:curl -X PURGE http://172.16.252.187/index.html

Varnish开启后端故障保护

sub vcl_backend_response { # 自定义缓存文件的缓存时长,即TTL值

if (bereq.url ~ "\.(jpg|jpeg|gif|png)$") {

set beresp.ttl = 5s;

}

if (bereq.url ~ "\.(html|css|js)$") {

set beresp.ttl = 1200s;

}

# if (beresp.http.Set-Cookie) { # 定义带Set-Cookie首部的后端响应不缓存,直接返回给客户端

#

set beresp.grace = 30m;

return(deliver);

# }

}

# 以上配置表示varnish将会将失效的缓存对象再多保留30分钟,此值等于最大的req.grace值即可;

# 而根据后端主机的健康状况,varnish可向前端请求提供30分钟过期内容

 

 

 

 

 

 

posted @ 2017-12-07 17:47 大天使彦 阅读(...) 评论(...) 编辑 收藏