Web Cache

       缓存:缓存就是数据交换的缓冲区(cache),当某一硬件要读取数据时,会首先从缓存中查找需要的数据,如果找到了则直接执行,找不到的话则从内存中找。由于缓存的运行速度比内存快很多,故缓存的作用就是帮助硬件更快的运行。

       程序具有局部性:时间局部性,空间局部性。

       缓存存储方式:key-value

              Key:访问路径,url,hash

              Value:web content

       热点数据:局部性的数据;

       命中率:hit/(hit+miss)

              文档命中率:从文档个数进行衡量;

              字节命中率:从内容大小进行衡量

       注意:

              缓存对象:生命周期;定期清理;

              缓存空间耗尽:LRU(最近最少使用)

              可缓存,不可缓存(用户私有数据)

       缓存处理的步骤:

              接收请求 à 解析请求(提取请求的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: faiy89345

常用的开源解决方案:squid ,varnish,本文重点介绍varnish;

 

Varnish

       程序架构图:

              Linux自学笔记——varnish_varnish                                               

       管理进程:编译vcl并应用新配置;监控varnish;初始化varnish;CLI接口

       Child/cache

              Acceptor:接收新的连接请求;

              worker threads:处理用户请求;

              Expiry:清理缓存中的过期对象;

       日志:Shared Memory Log,共享内存日志大小默认一般为90MB,分为两部分,前一部分为计数器,后一部分请求相关数据;

       vcl:Varnish Configuration Language

              保存策略配置接口;

              基于“域”的简单编程语言;

       内存分配和回收:

              malloc(),free()

       varnish如何存储缓存对象?可以通过以下三种方式;

              file:单个文件;不支持持久机制;

              malloc:内存

              persistent:基于文件的持久存储;

       varnish的主要配置文件及工具:

              Linux自学笔记——varnish_cache_02

       配置varnish的三种应用:

1.      Varnish应用程序的命令行参数;

监听的socket,使用的存储类型等等;额外的配置参数;

-p parm=value

-r  param,param,…:设定只读参数列表;

 

/etc/varnish/varnish.params

   Linux自学笔记——varnish_varnish_03

2.      –p选项指明的参数:

运行时参数:

   也可在程序运行中,通过其CLI进行配置;

3.      vcl:配置缓存系统的缓存机制;

通过vcl配置文件进行配置;

先编译,后应用;

   依赖于c编译器;

      

Note

1)      varnish是一个web代理缓存服务器,既然是代理就要代表原始web服务器工作,所以,接收的用户请求会首先发送到varnish,那这样一来按理说就应该监听在80端口;其实不然,为了防止和本机的80端口起冲突,varnish默认是监听在6081端口的;这是因为虽然varnish为代理缓存服务器,但并不会让其直接面向用户,而是通过前端的nignx或者haproxy将用户的请求反代至varnish,由varnish完成缓存管理,如果本地缓存没有,varnish再去查找原始服务器,如果有就直接返回给前端反代服务器。      

2)      6082端口为基于命令行工具去管理和控制varnish的接口,为了安全起见监听在本机的127.0.0.1的地址;

 

演示:使用varnish代理缓存后端web服务

实验环境:两台服务器,一台作varnish代理缓存服务器,一台作后端服务器;

Ip地址分配:

       varnish缓存服务器:192.168.19.203

       后端RS:192.168.19.143

1.      配置文件/etc/varnish/varnish.params如下,(一般默认不改变)

    Linux自学笔记——varnish_cache_04

2.      编辑/etc/varnish/default.val文件,反向代理至后端服务器;

    Linux自学笔记——varnish_cache_05

3.      启动varnish服务并查看端口;

    Linux自学笔记——varnish_varnish_06

4.      访问网站测试;

    Linux自学笔记——varnish_cache_07

Note:后面需要加端口6081,

 

命令行工具:

varnishadm命令以及vcl配置文件的重载:

重载vcl配置文件:

       varnish_reload_vcl

varnishadm命令:

       varnishadm -S /etc/varnish/secret -T IP:PORT

       Linux自学笔记——varnish_proxy_08

 

vcl

       请求流程图:

              Linux自学笔记——varnish_varnish_09

state engine:各引擎之间存在一定程度上的相关性;前一个engine如果可以有多种下游engine,则说明engine需要用return指明要转移的下游engine;

              vcl_recv

              vcl_hash

              vcl_hit

              vcl_miss

              vcl_fetch

              vcl_deliver

              vcl_pipe

              vcl_pass

              vcl_error

       编程语言语法:

1)      //,#,/* */用于注释;会被编译器忽略;

2)      sub  $name:用于定义子例程;

sub vcl_recv {

 

}

3)      不支持循环;

4)      有众多内置的变量,变量可调用位置与state engine有密切相关性;

5)      支持终止语句,return(action):没有返回值;

6)      域专用 ;

7)      操作符:=,==,~,!,&&,||

条件判断语句:

   if (CONDTION) {

 

   } else {

 

   }

变量赋值:set  name=value

   unset  name

 

   req.http.HEADER:调用request报文中http报文中http协议的指定的HEADER首部

          req.http.X-Forwarded-For

          req.http.Auhtorization

          req.http.cookie

   req.request:请求方法

   client.ip:客户端IP;

 

state engine workflow(v3):

   vcl_recv --> vcl_hash --> vcl_hit --> vcl_deliver

   vcl_recv --> vcl_hash --> vcl_miss --> vcl_fetch --> vcl_deliver

   vcl_recv --> vcl_pass --> vcl_fetch --> vcl_deliver

   vcl_recv --> vcl_pipe

 

state engine(v4)

   vcl_recv

   vcl_pass

   vcl_pipe

   vcl_hash

   vcl_hit

   vcl_miss

 

   vcl_backend_fetch

   vcl_backend_response

   vcl_backend_error

 

   vcl_purge

   vcl_synth

 

   https://www.varnish-software.com/book/4.0/chapters/VCL_Basics.html

 

   sub vcl_recv {

           if (req.method == "PRI") {

                 /* We do not support SPDY or HTTP/2.0 */

                 return (synth(405));

          }

 

          if (req.method != "GET" &&

                 req.method != "HEAD" &&

                 req.method != "PUT" &&

                 req.method != "POST" &&

                 req.method != "TRACE" &&

                 req.method != "OPTIONS" &&

                 req.method != "DELETE") {

                        /* Non-RFC2616 or CONNECT which is weird. */

                        return (pipe);

          }

 

          if (req.method != "GET" && req.method != "HEAD") {

                 /* We only deal with GET and HEAD by default */

                 return (pass);

          }

          if (req.http.Authorization || req.http.Cookie) {

                 /* Not cacheable by default */

                 return (pass);

          }

                 return (hash);

   }

定义在vcl_deliver中,向响应给客户端的报文添加一个自定义首部X-Cache

   if (obj.hits>0) {

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

   } else {

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

   }

演示:通过判断想应报文缓存命中的次数来判断是否命中缓存;

1)      编辑配置文件/etc/varnish/default.vcl;

    Linux自学笔记——varnish_cache_10

2)      重载配置文件,过程如下;

    Linux自学笔记——varnish_varnish_11

3)      浏览器测试;

   Linux自学笔记——varnish_cache_12 

 

Varnish中的内置变量:

       变量种类:

              client

              erver

              req

              resp

              bereq

              beresp

              obj

              storage

       bereq

              bereq.http.HEADERS: 由varnish发往backend server的请求报文的指定首部;

              bereq.request:请求方法;

              bereq.url

              bereq.proto:协议的版本

              bereq.backend:指明要调用的后端主机;

       beresp

              beresp.proto:后端服务器响应的协议版本

              beresp.status:后端服务器的响应的状态码

              beresp.reason:原因短语;

              beresp.backend.ip:后端响应的ip

              beresp.backend.name:BE主机的主机名

              beresp.http.HEADER: 从backend server响应的报文的首部;

              beresp.ttl:后端服务器响应的内容的余下的生存时长;

       obj

              obj.ttl: 对象的ttl值;

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

       server

              server.ip

              server.hostname

       req

       resp

 

演示1:强制对某类资源的请求不检查缓存,之类以login和admin为例;

1)      提供测试页面环境;

    Linux自学笔记——varnish_proxy_13

2)      访问测试;

    Linux自学笔记——varnish_varnish_14

    可以看出,第一次没有命中,但是以后都能命中的。

3)      现在我们修改配置文件,在vcl_recv下添加以下内容,强行对此资源不检查缓存;

    Linux自学笔记——varnish_varnish_15

4)      重新装载配置文件,测试(装载配置文件过程,这里不再多说)

    Linux自学笔记——varnish_varnish_16

可以看出,这两次访问,都没有击中缓存;

 

演示2:设定多个后端主机,并分别以负载均衡和动静分离的方式调度;

动静分离方式调度:

1)      提供RS2的php页面测试环境;(注意,要安装httpd+php)

    Linux自学笔记——varnish_proxy_17

2)      编辑配置文件,添加内容如下;

    Linux自学笔记——varnish_proxy_18

3)      重载配置文件,并测试;

访问.php文件时:

Linux自学笔记——varnish_varnish_19

Note:php右上角的图片没有显示出来,是因为静态资源发往了192.168.19.144这台主机,而这台主机上又没有这张图片。所以显示不出来;

访问html文件时:

Linux自学笔记——varnish_cache_20

 

负载均衡调度:使用前需要导入:import director

    1)      编辑配置文件,首先导入import director模块,内容如下;

    Linux自学笔记——varnish_proxy_21

    2)      重载配置文件,测试;

    Linux自学笔记——varnish_varnish_22

Note:varnish的负载均衡调度是对请求的不同资源的负载均衡调度,请求统一资源,它只会调度至一开始调度的服务器;

 

定义健康状态监测;

backend server的定义:

backend name {

            .attribute = "value";

       }    

 

       .host: BE主机的IP;

       .port:BE主机监听的PORT;

       .probe: 对BE做健康状态检测;

       .max_connections:并连接最大数量;

后端主机的健康状态检测方式:

       probe name {

.attribute = "value";

       }    

              .url: 判定BE健康与否要请求的url;

              .expected_response:期望响应状态码;默认为200;

              .request:发出的具体请求

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

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

              .interval:监测额度;

              .timeout:超时时长;

 

演示3:设定varnish对后端主机做健康状态监测;

1)      使用varnishadm命令可以查看后端主机是否做了检测状态监测;

    Linux自学笔记——varnish_cache_23

2)      编辑配置文件/etc/varnish/default.vcl,定义健康状态监测的probe;

方法一:

Linux自学笔记——varnish_proxy_24

  方法二:

       Linux自学笔记——varnish_cache_25

3)      重载配置文件,查看后端主机是否开启健康状态检测;

    Linux自学笔记——varnish_cache_26

4)      浏览器访问测试看是否能正常访问;

    Linux自学笔记——varnish_cache_27

 

补充:

移除单个缓存对象

       purge用于清理缓存中的某特定对象及其变种(variants),因此,在有着明确要修剪的缓存对象时可以使用此种方式。HTTP协议的PURGE方法可以实现purge功能,不过,其仅能用于vcl_hit和vcl_miss中,它会释放内存工作并移除指定缓存对象的所有Vary:-变种,并等待下一个针对此内容的客户端请求到达时刷新此内容。另外,其一般要与return(restart)一起使用。

下面是个在VCL中配置的示例。

acl purgers {

"127.0.0.1";

       "192.168.0.0"/24;

}

 

sub vcl_recv {

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

              if (!client.ip ~ purgers) {

                     error 405 "Method not allowed";

              }

              return (lookup);

       }

}

sub vcl_hit {

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

              purge;

              error 200 "Purged";

       }

}

sub vcl_miss {

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

              purge;

              error 404 "Not in cache";

       }

}

sub vcl_pass {

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

              error 502 "PURGE on a passed object";

       }

}

 

客户端在发起HTTP请求时,只需要为所请求的URL使用PURGE方法即可,其命令使用方式如下:

# curl -I -X PURGE http://varniship/path/to/someurl

 

补充资料:

       varnish的线程模型:

              cache-worker线程

              cache-main线程:此线程只有一个,用于启动caceh;

              ban luker

              acceptor

              epoll:线程池管理器

              expire:清理过期缓存

 

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

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

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

                     thread_pool_min

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

 

       varnish的param查看及改变:

              param.show [-l] [param]

              param.set [param] [value]

 

       varnish的命令行工具:

              varnishadm,

 

              varnishtop: 内存日志区域查看工具

                     RxHeader User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36

              其中:

                     RxHeader:称为tag, 基于tag过滤,可使用-i或-x选项;

                     User-Agent起始的内容:称为日志信息,可使用-I或-X选项进行过滤;

 

                     -I regexp: 仅显示被模式匹配到的条目

                     -X regexp:仅显示不被模式匹配到的条目

                     -C: 忽略字符大小写;

                     -d: 显示已有日志;

              varnishstat

                     -f field, field, ...

                     -l: 列出所有可用字段

                     -x: xml输出格式

                     -j: json输出格式

              varnishlog, varnishncsa