什么是缓存,为什么要缓存 

    在客户端和原始服务器端之间存在一个可以自己构建响应报文的服务器,这个服务器通常就是缓存服务器.为什么要缓存服务器?在互联网上传输数据(这里指web浏览),数据传输通常有三个阶段:第一阶段:原始服务器到互联网;第二阶段:网络间的层层路由;第三阶段:网络到请求数据的客户端.为了提高用户体验,加速用户访问原始服务器,可以把热点数据放在缓存服务器上,缓存服务器通常放在离用户比较近的地方,如:cdn机房等,在同一个网域内客户端请求相同的数据可以直接从缓存服务器中取得,减少数据冗余,用户请求数据不用去原始服务器了,减少网络间的路由时间和服务器的压力.用户请求的数据有多少是可以从缓存服务器上取得的,取决于缓存的命中率;命中率通常又分为文档命中率和字节命中率(对于中等规模的web来说,40%左右的命中率就可以了);缓存类型又分为:私有缓存和公共缓存;用户访问互联网的过程-->检查私有缓存,有就返回数据,没有就去请求二级代理,二级代理有就返回数据,没有就请求一级代理,最后请求原始服务器;如果有多个二级代理或一级代理,并且多个二级代理可以互相查找数据(需配置)还可以向一级代理(父代理)查找数据,这个方式叫做内容路由,内容路由需要通过ICP(缓存路由协议)通信;

缓存处理的具体步骤:

    接受请求-->解析请求(代理功能)-->查询缓存-->缓存内容的新鲜程度(非必须)-->构建响应报文(缓存服务器有数据)-->发送响应记录日志

缓存内容的过期检查方式

    响应首部:文档过期机制:HTTP/1.0 Expires(过期时间:2014-05-08 19:00:00过期);HTTP/1.1 Cache-Control(max-age = 300s 300s过期)

    请求首部:条件式请求机制:根据时间戳:比较缓存服务器和原始服务器的数据时间戳是否一致(mtime:If-Modified-Since); 根据扩展标记:(If-None-Match):比较缓存服务器和原始服务器的数据扩展标记是否一致

    结合机制:文档过期机制+条件式请求机制

原始服务器或缓存服务器控制缓存的首部(响应首部)

    max-age 控制私有缓存的过期时间  如:max-age=3600

    s-maxage 控制公共缓存,同上

    no-store :控制位,表示不能缓存,只要后端服务器的报文首部有no-store首部,缓存服务器就不能缓存,这是遵循了缓存协议的控制规定

    no-cache:能缓存,不能直接使用此缓存对象,意思是缓存对象在使用之前必须做新鲜度验证

    must-revalidate:必须进行新鲜度验证.同上

    private : 用户的私有数据

    public: 用户的公开数据


vanish  

   varnish是一个轻量级的web的缓存服务器,他对nginx或apache做缓存,单个varnish的连接请求并发量大致在5000个左右,所以在大规模的站点中,可能还是有squie主导.在中小企业的规模中做代理缓存时,varnish比较常见,varnish不支持使用过大的内存空间.

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

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

    Child进程包含多种类型的线程,常见的如:

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

Worker线程:child进程会为每个会话启动一个worker线程,因此,在高并发的场景中可能会出现数百个worker线程甚至更多;

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

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


540d2a39-5985-4b11-81a6-d353560d1e7a

如上图:

   Management功能

   Command line 提供命令行接口

   Chile process mgmt 管理子进程

   Initialisation  配置应用初始化

    Child/cache

    Command line 命令行接口

    Storage/hashing 存储hash功能

    Log/stats 日志

    Accept 接受并处理用户请求

    Backend communication 如果本地没有缓存请求的内容,就自己构建请求报文至原始服务器

    Worker threads 负责处理用户请求

    Object expiry  清楚过期缓存对象

   varnish管理接口

    CLI interface  命令行接口

    Telnet interface  telnet接口,为了安全,目前都禁用了

    Web interface  基于web界面的监控

    Log file 默认情况下varnish的日志是存在内存中的,想要存在磁盘中,需要自己配置

    vamishlog  日志格式

vamishstat 获取当前状态信息

vamishhist 历史性信息

vamishtop  监控服务状态的

vamishncsa 日志格式

    VCL complier VCL编译器

Varnish Configuration Language (VCL)是varnish配置缓存策略的工具,它是一种基于“域”(domain specific)的简单编程语言,它支持有限的算术运算和逻辑运算操作、允许使用正则表达式进行字符串匹配、允许用户使用set自定义变量、支持if判断语句,也有内置的函数和变量等。使用VCL编写的缓存策略通常保存至.vcl文件中,其需要编译成二进制的格式后才能由varnish调用。事实上,整个缓存策略就是由几个特定的子例程如vcl_recv、vcl_fetch等组成,它们分别在不同的位置(或时间)执行,如果没有事先为某个位置自定义子例程,varnish将会执行默认的定义。

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


varnish的重要概念:varnish的状态机 state engin 如下图1-1所示

52589699-92e9-4c23-9b71-9a4c3eb2267c

8bb74b89-ffb3-4e9c-8217-66a8a959582f

95399287-b881-4834-802a-8a82a70635db

用户请求数据:

    如果请求的数据是缓存服务器不认识的,直接交给pipe;

    如果是不可缓存的数据,直接交给pass,pass交给fetdh,由fetch构建请求报文交给后端服务器,后端服务器响应报文后,fetch看该报文是否可缓存,如果可缓存就先缓存然后返回给deliver.如果不能缓存就直接交给deliver

    如果是可缓存数据,先查看hash表,如果hash表中有数据,则命中(hit),然后由deliver返回给客户端

如果是可缓存数据,先查看hash表,如果hash表中没有数据,则不命中(miss),miss交给fetch,fetch向后端服务器请求数据

从上面可以知道

    缓存服务器的状态引擎有:vcl_recv,vcl_pass,vcl_hash,vcl_pipe,vcl_hit,vcl_miss,vcl_fetch,vcl_deliver,每一个状态的下一步如何处理由return决定,在vcl_recv阶段,return(pipe)就交给vcl_pipe,return(lookup)就交给vcl_hit或vcl_miss

VCL的内置函数

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

regsub(str,regex,sub) : 将str中的内容,能够被regex匹配到的替换成sub,只替换第一个

regsuball(str,regex,sub):这两个用于基于正则表达式搜索指定的字符串并将其替换为指定的字符串;但regsuball()可以将str中能够被regex匹配到的字符串统统替换为sub,regsub()只替换一次;

ban(expression):

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

purge:从缓存中挑选出某对象以及其相关变种一并删除,这可以通过HTTP协议的PURGE方法完成;

hash_data(str):

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

return(restart):重新运行整个VCL,即重新从vcl_recv开始进行处理;每一次重启都会增加req.restarts变量中的值,而max_restarts参数则用于限定最大重启次数。

vcl_recv子例程(状态引擎)

    cl_recv是在Varnish完成对请求报文的解码为基本数据结构后第一个要执行的子例程,它通常有四个主要用途:

(1)修改客户端数据以减少缓存对象差异性;比如删除URL中的www.等字符;

(2)基于客户端数据选用缓存策略;比如仅缓存特定的URL请求、不缓存POST请求等;

(3)为某web应用程序执行URL重写规则;

(4)挑选合适的后端Web服务器;

可以使用下面的终止语句,即通过return()向Varnish返回的指示操作:

pass:绕过缓存,即不从缓存中查询内容或不将内容存储至缓存中;

pipe:不对客户端进行检查或做出任何操作,而是在客户端与后端服务器之间建立专用“管道”,并直接将数据在二者之间进行传送;此时,keep-alive连接中后续传送的数据也都将通过此管道进行直接传送,并不会出现在任何日志中;

lookup:在缓存中查找用户请求的对象,如果缓存中没有其请求的对象,后续操作很可能会将其请求的对象进行缓存;

error:由Varnish自己合成一个响应报文,一般是响应一个错误类信息、重定向类信息或负载均衡器返回的后端web服务器健康状态检查类信息;

vcl_recv也可以通过精巧的策略完成一定意义上的安全功能,以将某些特定的***扼杀于摇篮中。同时,它也可以检查出一些拼写类的错误并将其进行修正等。

Varnish默认的vcl_recv专门设计用来实现安全的缓存策略,它主要完成两种功能:

(1)仅处理可以识别的HTTP方法,并且只缓存GET和HEAD方法;

(2)不缓存任何用户特有的数据;

安全起见,一般在自定义的vcl_recv中不要使用return()终止语句,而是再由默认vcl_recv进行处理,并由其做出相应的处理决策。

vcl_fetch子例程(状态引擎)

    如前面所述,相对于vcl_recv是根据客户端的请求作出缓存决策来说,vcl_fetch则是根据服务器端的响应作出缓存决策。在任何VCL状态引擎中返回的pass操作都将由vcl_fetch进行后续处理。vcl_fetch中有许多可用的内置变量,比如最常用的用于定义某对象缓存时长的beresp.ttl变量。通过return()返回给varnish的操作指示有:

(1)deliver:缓存此对象,并将其发送给客户端(经由vcl_deliver);

(2)hit_for_pass:不缓存此对象,但可以导致后续对此对象的请求直接送达到vcl_pass进行处理;

(3)restart:重启整个VCL,并增加重启计数;超出max_restarts限定的最大重启次数后将会返回错误信息;

(4)error code [reason]:返回指定的错误代码给客户端并丢弃此请求;

默认的vcl_fetch放弃了缓存任何使用了Set-Cookie首部的响应。


varnish的后端存储

varnish支持多种不同类型的后端存储,这可以在varnishd启动时使用-s选项指定。后端存储的类型包括:

(1)file:使用特定的文件存储全部的缓存数据,并通过操作系统的mmap()系统调用将整个缓存文件映射至内存区域(如果条件允许);

(2)mal loc:使用malloc()库调用在varnish启动时向操作系统申请指定大小的内存空间以存储缓存对象;

(3)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}

file中的granularity用于设定缓存空间分配单位,默认单位是字节,所有其它的大小都会被圆整。

注:为了增大缓存命中率,要把请求中不必要cookie的都移除,响应中不必要setcookie都移除要移,除用户请求中的cookie,要在vcl_recv中进行,要移除set_cookie,要在vcl_fetch中进行


1.安装varnish

    rpm -ivh varnish-3.0.4-1.el6.x86_64.rpm varnish-docs-3.0.4-1.el6.x86_64.rpm varnish-libs-3.0.4-1.el6.x86_64.rpm

    rpm -ql varnish

规划:varnish服务器

公网IP:192.168.1.107

    内网IP:172.16.21.1

后端web服务器,web1:172.16.21.2

后端web服务器,web2:172.16.21.3

2.配置varnish

    vim /etc/sysconfig/varnish

VARNISH_LISTEN_PORT=80

VARNISH_STORAGE="malloc,100M"

注:修改配置文件后都要重新加载配置文件

vanishadmin -S /etc/varnish/secreet -T 172.0.0.1:6082

    varnish>vcl.reload  test1 default.vcl

3.定义后端服务器

vim /etc/varnish/default.vcl

backend default {

  .host = "172.16.21.3";

  .port = "80";

}

4.启动varnish,访问192.168.1.107

    service varnish start

5.使用varnish的客户端管理工具

    vanishadmin -S /etc/varnish/secreet -T 172.0.0.1:6082

    varnish>help

200       

help [command]

ping [timestamp]

auth response

quit

banner

status

start

stop

vcl.load <configname> <filename>

vcl.inline <configname> <quoted_VCLstring>

vcl.use <configname>

vcl.discard <configname>

vcl.list

vcl.show <configname>

param.show [-l] [<param>]

param.set <param> <value>

panic.show

panic.clear

storage.list

backend.list

backend.set_health matcher state

ban.url <regexp>

ban <field> <operator> <arg> [&& <field> <oper> <arg>]...

ban.list

6.查看缓存是否命中,并且从哪里命中

sub vcl_deliver {

        if (obj.hits > 0){

        set resp.http.X-Cache = "HIT" + server.ip;  resp表示响应,http首部,名称X-Cache

        } else {

        set resp.http.X-Cache = "MISS";

        }  

}

7.对指定网页不做缓存,如test.html

sub vcl_recv {

        if (req.url ~ "test.html"){

        return(pass);

        }  

        }  

8.自定义缓存时长

    sub vcl_fetch {

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

    set beresp.ttl = 7200s;

}

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

    set beresp.ttl = 1200s;

}

}

9.清除缓存

acl purger {

        "127.0.0.1";

        "192.168.0.0"/16;

        }

sub vcl_recv {

        if (req.request == "QC") {

        if (!client.ip ~ purger) {

        error 405 "error";

        }

        return(lookup);

        }

        }

sub vcl_hit {

        if (req.request == "QC"){

        purge;

        error 200 "sesscee";

        }

        }

sub vcl_miss {

        if (req.request == "QC"){

        error 404 "error miss";

        }

        }

sub vcl_pass {

        if (req.request == "QC"){

        error 502 "502";

        }

10.后端主机健康状态检测

backend web2{

    .host = "www.a.com";

    .probe = {

    .url = "/.test.html";  没有指定具体的内容表示请求到此网页就代表后端服务器正常

    .interval = 1s;

    .window = 5; 一共测试5次

    .threshold = 2; 5次中有两次错误就认为是失败的

}

}

11.多台后端主机

backend web1 {

.host = "backweb1.magedu.com";

.port = "80";

}

director webservers random {  random:调度方法,加权随机

  .retries = 5;  重试次数,每个主机健康状态检测的重试次数

  {

    .backend = web1; 后端real server

    .weight  = 2; 权重

  }

  {

    .backend  = {

      .host = "backweb2.magedu.com";

.port = "80";

    }

.weight         = 3;

  }

}

backend web1 {

    .host = "172.16.21.2"

    .port = "80";

}

backend web2 {

    .host = "172.16.21.3";

    .weight = "80";

}

director webserver random {

    {

    .backend = web1;

    .weight = 2;

}

    {

    .backend = web2;

    .weight = 3;

}

}

sub vcl_recv {

    set req.backend = webser; 上面设置了一定要引用,否则会报错

}

167097250

12.动静分离雏形

backend web1 {

        .host = "172.16.21.2";

        .port = "80" ;

        }  

backend web2 {

        .host = "172.16.21.3";

        .port = "80";

        }

sub vcl_recv {

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

        set req.backend = web1;

        }else{

        set req.backend = web2;

        }  

        }

13.varnishadm命令

    vanishadm

varnish> storage.list

varnish> backend.list

varnish> ban.url ^/shop/.*    清理缓存,ban.url后面跟一个正则表达式,慎用,任何清理缓存的操作都要慎用

14.varnishstats命令

varnishstat --help

varnishstat: invalid option -- '-'

usage: varnishstat [-1lV] [-f field_list] [-n varnish_name] [-w delay]

    -1                           # Print the statistics to stdout.

    -f field_list                # Comma separated list of fields to display.

                                 # If it starts with '^' it is used as an exclusion list

    -l                           # Lists the available fields to use with the -f option

    -n varnish_name              # The varnishd instance to get logs from

    -V                           # Display the version number and exit

    -w delay                     # Wait delay seconds between updates.  Default is 1 second. Can also be be used with -1, -x or -j for repeated output.

    -x                           # Print statistics to stdout as XML.

    -j                           # Print statistics to stdout as JSON.

14.把内存中的日志保存到磁盘中

service varnishlog start

   tail /var/log/varnish/varnish.log

15.防止盗链

if (req.http.referer) {
               if (  !(req.http.referer ~ "http://.*a\.com" 
                    || req.http.referer ~ "http://.*b\.com\.cn"
                    || req.http.referer ~ "http://.*c\.cn"
                    || req.http.referer ~ "http://.*google\.com"
                    || req.http.referer ~ "http://.*yahoo\.cn"
                    || req.http.referer ~ "http://.*google\.cn"
                 )) {        
                       set req.http.host = "img.test.com";
                       set req.url = "/images/default.jpg";
               }

          }

          else {
                    lookup;
          }