一、varnish原理:

1Varnish简介:

varnish缓存是web应用加速器,同时也作为http反向缓存代理。你可以安装varnish在任何http的前端,同时配置它缓存内容。与传统的 squid 相比,varnish 具有性能更高、速度更快、管理更加方便等诸多优点。有一部分企业已经在生产环境中使用其作为旧版本的squid的替代方案,以在相同的服务器成本下提供更好的缓存效果,Varnish更是作为CDN缓存服务器的可选服务之一。

根据官网的介绍,Varnish的主要特性如下:https://www.varnish-cache.org/

1.缓存位置:可以使用内存也可以使用磁盘。如果要使用磁盘的话推荐SSDRAID1

2.日志存储:日志也存储在内存中。存储策略:固定大小,循环使用

3.支持虚拟内存的使用。

4.有精确的时间管理机制,即缓存的时间属性控制。

5.状态引擎架构:在不同的引擎上完成对不同的缓存和代理数据进行处理。可以通过特定的配置语言设计不同的控制语句,以决定数据在不同位置以不同方式缓存,在特定的地方对经过的报文进行特定规则的处理。

6.缓存管理:以二叉堆格式管理缓存数据,做到数据的及时清理。

2VarnishSquid的对比

相同点

都是一个反向代理服务器;

都是开源软件;

Varnish的优势

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

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

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

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

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

Varnish的劣势

1varnish进程一旦Crash或者重启,缓存数据都会从内存中完全释放,此时所有请求都会发送到后端服务器,在高并发情况下,会给后端服务器造成很大压力;

2varnish使用中如果单个url的请求通过HA/F5等负载均衡,则每次请求落在不同的varnish服务器中,造成请求都会被穿透到后端;而且同样的请求在多台服务器上缓存,也会造成varnish的缓存的资源浪费,造成性能下降;

Varnish劣势的解决方案

针对劣势一:在访问量很大的情况下推荐使用varnish的内存缓存方式启动,而且后面需要跟多台squid/nginx服务器。主要为了防止前面的varnish服 务、服务器被重启的情况下,大量请求穿透varnish,这样squid/nginx可以就担当第二层CACHE,而且也弥补了varnish缓存在内存中重启都会释放的问题;

针对劣势二:可以在负载均衡上做url哈希,让单个url请求固定请求到一台varnish服务器上;

3)使用varnish作为web代理缓存的原理 

varnish是一个http反向代理的缓存。它从客户端接收请求然后尝试从缓存中获取数据来响应客户端的请求,如果varnish不能从缓存中获得数据来响应客户端,它将转发请求到后端(backend servers,获取响应同时存储,最后交付给客户端。 

如果varnish已经缓存了某个响应,它比你传统的后端服务器的响应要快很多,所以你需要尽可能是更多的请求直接从varnish的缓存中获取响应。 

varnish决定是缓存内容或者是从后端服务器获取响应。后端服务器能通过http响应头中的Cache-Control来同步varnish缓存内容。在某些条件下varnish将不缓存内容,最常见的是使用cookie。当一个被标记有cookie的客户端web请求,varnish默认是不缓存。这些众多的varnish功能特点都是可以通过写vcl来改变的。

5)简单架构:

Varnish 分为 management 进程和child 进程;

Management进程:对子进程进行管理,同时对VCL配置进行编译,并应用到不同的状态引擎。

Child进程:生成线程池,负责对用户请求进行处理,并通过hash查找返回用户结果。

6varnish主要配置部分:

varnish配置主要分为:后端配置,ACL配置,probes配置,directors配置,核心子程序配置几大块。其中后端配置是必要的,在多台服务器中还会用到directors配置,核心子程序配置。

后端配置:即给varnish添加反代服务器节点,最少配置一个。

ACL配置:即给varnish添加访问控制列表,可以指定这些列表访问或禁止访问。

probes配置:即给varnish添加探测后端服务器是否正常的规则,方便切换或禁止对应后端服务器。

directors配置:即给varnish添加负载均衡模式管理多个后端服务器。

核心子程序配置:即给varnish添加后端服务器切换,请求缓存,访问控制,错误处理等规则。

7VCL中内置预设变量:变量(也叫object)

wKioL1jU0X6zOQi2AAFcRgx4SN4973.png

reqThe request object,请求到达时可用的变量(客户端发送的请求对象)

bereqThe backend request object,向后端主机请求时可用的变量

berespThe backend response object,从后端主机获取内容时可用的变量(后端响应请求对象)

respThe HTTP response object,对客户端响应时可用的变量(返回给客户端的响应对象)

obj:存储在内存中时对象属性相关的可用的变量(高速缓存对象,缓存后端响应请求内容)

 

预设变量是系统固定的,请求进入对应的vcl子程序后便生成,这些变量可以方便子程序提取,当然也可以自定义一些全局变量。

当前时间:

now :作用:返回当前时间戳。

 

客户端:(客户端基本信息)

client.ip:返回客户端IP地址。

注:原client.port已经弃用,如果要取客户端请求端口号使用 std.port(client.ip),需要import std;才可以使用std

client.identity:用于装载客户端标识码。

 

服务器:(服务器基本信息)

注:原server.port已经弃用,如果要取服务器端口号使用 std.port(server.ip),需要import std;才可以使用std

server.hostname:服务器主机名。

server.identity:服务器身份标识。

server.ip:返回服务器端IP地址。

 

req :(客户端发送的请求对象)

req:整个HTTP请求数据结构

req.backend_hint:指定请求后端节点,设置后 bereq.backend 才能获取后端节点配置数据

req.can_gzip:客户端是否接受GZIP传输编码。

req.hash_always_miss:是否强制不命中高速缓存,如果设置为true,则高速缓存不会命中,一直会从后端获取新数据。

req.hash_ignore_busy:忽略缓存中忙碌的对象,多台缓存时可以避免死锁。

req.http:对应请求HTTPheader

req.method:请求类型(如 GET , POST)。

req.proto:客户端使用的HTTP协议版本。

req.restarts:重新启动次数。默认最大值是4

req.ttl:缓存有剩余时间。

req.url:请求的URL

req.xid:唯一ID

 

bereq:(发送到后端的请求对象,基于req对象)

bereq:整个后端请求后数据结构。

bereq.backend:所请求后端节点配置。

bereq.between_bytes_timeout:从后端每接收一个字节之间的等待时间(秒)。

bereq.connect_timeout:连接后端等待时间(秒),最大等待时间。

bereq.first_byte_timeout:等待后端第一个字节时间(秒),最大等待时间。

bereq.http:对应发送到后端HTTPheader信息。

bereq.method:发送到后端的请求类型(如:GET , POST)。

bereq.proto:发送到后端的请求的HTTP版本。

bereq.retries:相同请求重试计数。

bereq.uncacheable:无缓存这个请求。

bereq.url:发送到后端请求的URL

bereq.xid:请求唯一ID

 

beresp:(后端响应请求对象)

beresp:整个后端响应HTTP数据结构。

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

beresp.backend.name:响应后端配置节点的name

beresp.do_gunzip:默认为 false 。缓存前解压该对象

beresp.do_gzip:默认为 false 。缓存前压缩该对象

beresp.grace:设置当前对象缓存过期后可额外宽限时间,用于特殊请求加大缓存时间,当并发量巨大时,不易设置过大否则会堵塞缓存,一般可设置 1m 左右,当beresp.ttl=0s时该值无效。

beresp.http:对应的HTTP请求header

beresp.keep:对象缓存后带保持时间

beresp.proto:响应的HTTP版本
beresp.reason:由服务器返回的HTTP状态信息

beresp.status:由服务器返回的状态码

beresp.storage_hint:指定保存的特定存储器

beresp.ttl:该对象缓存的剩余时间,指定统一缓存剩余时间。

beresp.uncacheable:继承bereq.uncacheable,是否不缓存

 

OBJ :(高速缓存对象,缓存后端响应请求内容)

obj.grace:该对象额外宽限时间

obj.hits:缓存命中次数,计数器从1开始,当对象缓存该值为1,一般可以用于判断是否有缓存,当前该值大于0时则为有缓存。

obj.http:对应HTTPheader

obj.protoHTTP版本

obj.reason:服务器返回的HTTP状态信息

obj.status:服务器返回的状态码

obj.ttl:该对象缓存剩余时间(秒)

obj.uncacheable:不缓存对象

 

resp :(返回给客户端的响应对象)

resp:整个响应HTTP数据结构。

resp.http:对应HTTPheader

resp.proto:编辑响应的HTTP协议版本。

resp.reason:将要返回的HTTP状态信息。

resq.status:将要返回的HTTP状态码。

 

存储

storage.<name>.free_space:存储可用空间(字节数)。

storage.<name>.used_space:存储已经使用空间(字节数)。

storage.<name>.happy:存储健康状态。

 

8、特定功能性语句

ban(expression):清除指定对象缓存

call(subroutine):调用子程序,如:call(name);

hash_data(input):生成hash键,用于制定hash键值生成结构,只能在vcl_hash子程序中使用。调用 hash_data(input) 后,即这个hash为当前页面的缓存hash键值,无需其它获取或操作,如:

sub vcl_hash{

       hash_data(client.ip);

       return(lookup);

}

注意:return(lookup); 是默认返回值,所以可以不写。

new():创建一个vcl对象,只能在vcl_init子程序中使用。

return():结束当前子程序,并指定继续下一步动作,如:return (ok); 每个子程序可指定的动作均有不同。

rollback():恢复HTTP头到原来状态,已经弃用,使用 std.rollback() 代替。

synthetic(STRING):合成器,用于自定义一个响应内容,比如当请求出错时,可以返回自定义 404 内容,而不只是默认头信息,只能在 vcl_synth vcl_backend_error 子程序中使用,如:

sub vcl_synth {

    //自定义内容

    synthetic ({"

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html lang="zh-cn">

   <head>

   <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

      <title>error</title>

   </head>

   <body>

      <h1>Error</h1>

      <h3>这只是一个测试自定义响应异常内容</h3>

   </body>

</html>

    "});

    //只交付自定义内容

    return(deliver);

 

regsub(str, regex, sub):使用正则替换第一次出现的字符串,第一个参数为待处理字符串,第二个参数为正则表达式,第三个为替换为字符串。

regsuball(str, regex, sub):使用正则替换所有匹配字符串。参数与regsuball相同。

具体变量详见:

https://www.varnish-cache.org/docs/4.0/reference/vcl.html#reference-vcl

 

9return 语句:

return 语句是终止子程序并返回动作,所有动作都根据不同的vcl子程序限定来选用。

https://www.varnish-cache.org/docs/4.0/users-guide/vcl-built-in-subs.html

语法:return(action);

常用的动作:

abandon  放弃处理,并生成一个错误。

deliver  交付处理

fetch  从后端取出响应对象

hash  哈希缓存处理

lookup 查找缓存对象

ok 继续执行

pass  进入pass非缓存模式

pipe 进入pipe非缓存模式

purge 清除缓存对象,构建响应

restart 重新开始

retry 重试后端处理

synth(status code,reason) 合成返回客户端状态信息

 

10varnish中内置子程序有:

注:varnish内置子程序均有自己限定的返回动作  return (动作)不同的动作将调用对应下一个子程序。

vcl_recv子程序:

开始处理请求,通过 return (动作); 选择varnish处理模式,默认进入hash缓存模式(即return(hash);),缓存时间为配置项default_ttl(默认为 120秒)过期保持时间default_grace(默认为10秒)。该子程序一般用于模式选择,请求对象缓存及信息修改,后端节点修改,终止请求等操作。

可操作对象:(部分或全部值)

读:clientserverreqstorage

写:clientreq

返回值:

synth(status code,reason);  定义响应内容。

pass  进入pass模式,并进入vcl_pass子程序。

pipe  进入pipe模式,并进入vcl_pipe子程序。

hash  进入hash缓存模式,并进入vcl_hash子程序,默认返回值。

purge  清除缓存等数据,子程序先从vcl_hash再到vcl_purge

 

vcl_pipe子程序:

pipe模式处理,该模式主要用于直接取后端响应内容返回客户端,可定义响应内容返回客户端。该子程序一般用于需要及时且不作处理的后端信息,取出后端响应内容后直接交付到客户端不进入vcl_deliver子程序处理。

可操作对象:(部分或全部值)

读:clientserverbereqreqstorage

写:clientbereqreq

返回值:

synth(status code,reason);  定义响应内容。

pipe  继续pipe模式,进入后端vcl_backend_fetch子程序,默认返回值。

 

vcl_pass子程序:

pass模式处理,该模式类似hash缓存模式,仅不做缓存处理。

可操作对象:(部分或全部值)

读:clientserverreqstorage

写:clientreq

返回值:

synth(status code,reason);  定义响应内容。

fetch  继续pass模式,进入后端vcl_backend_fetch子程序,默认返回值。

 

vcl_hit子程序:

hash缓存模式时,存在hash缓存时调用,用于缓存处理,可放弃或修改缓存。

可操作对象:(部分或全部值)

读:clientserverobjreqstorage

写:clientreq

返回值:

restart 重启请求。

deliver 交付缓存内容,进入vcl_deliver子程序处理,默认返回值。

synth(status code,reason);  定义响应内容。

 

vcl_miss子程序:

hash缓存模式时,不存在hash缓存时调用,用于判断性的选择进入后端取响应内容,可以修改为pass模式。

可操作对象:(部分或全部值)

读:clientserverreqstorage

写:clientreq

返回值:

restart 重启请求。

synth(status code,reason);  定义响应内容。

pass 切换到pass模式,进入vcl_pass子程序。

fetch  正常取后端内容再缓存,进入vcl_backend_fetch子程序,默认返回值。

 

vcl_hash子程序:

hash缓存模式,生成hash值作为缓存查找键名提取缓存内容,主要用于缓存hash键值处理,可使用hash_data(string) 指定键值组成结构,可在同一个页面通过IPcookie生成不同的缓存键值。

可操作对象:(部分或全部值)

读:clientserverreqstorage

写:clientreq

返回值:

lookup 查找缓存对象,存在缓存进入vcl_hit子程序,不存在缓存进入vcl_miss子程序,当使用了purge清理模式时会进入vcl_purge子程序,默认返回值。

 

vcl_purge子程序:

清理模式,当查找到对应的缓存时清除并调用,用于请求方法清除缓存,并报告。

可操作对象:(部分或全部值)

读:clientserverreqstorage

写:clientreq

返回值:

synth(status code,reason);  定义响应内容。

restart 重启请求。

 

vcl_deliver子程序:

客户端交付子程序,在vcl_backend_response子程序后调用(非pipe模式),或vcl_hit子程序后调用,可用于追加响应头信息,cookie等内容。

可操作对象:(部分或全部值)

读:clientserverreqrespobjstorage

写:clientreqresp

返回值:

deliver 正常交付后端或缓存响应内容,默认返回值。

restart 重启请求。

 

vcl_backend_fetch子程序:

发送后端请求之前调用,可用于改变请求地址或其它信息,或放弃请求。

可操作对象:(部分或全部值)

读:serverbereqstorage

写:bereq

返回值:

fetch 正常发送请求到到后端取出响应内容,进入vcl_backend_response子程序,默认返回值。

abandon 放弃后端请求,并生成一个错误,进入vcl_backend_error子程序。

 

vcl_backend_response子程序:

后端响应后调用,可用于修改缓存时间及缓存相关信息。

可操作对象:(部分或全部值)

读:serverbereqberespstorage

写:bereqberesp

返回值:

deliver 正常交付后端响应内容,进入vcl_deliver子程序,默认返回值。

abandon 放弃后端请求,并生成一个错误,进入vcl_backend_error子程序。

retry 重试后端请求,重试计数器加1,当超过配置中max_retries值时会报错并进入vcl_backend_error子程序。

 

vcl_backend_error子程序:

后端处理失败调用,异常页面展示效果处理,可自定义错误响应内容,或修改beresp.statusberesp.http.Location重定向等。

可操作对象:(部分或全部值)

读:serverbereqberespstorage

写:bereqberesp

返回值:

deliver 只交付 sysnthetic(string) 自定义内容,默认返回后端异常标准错误内容。

retry 重试后端请求,重试计数器加1,当超过配置中max_retries值时会报错并进入vcl_backend_error子程序。

 

vcl_synth子程序:

自定义响应内容。可以通过synthetic()和返回值 synth 调用,这里可以自定义异常显示内容,也可以修改resp.statusresp.http.Location重定向。

可操作对象:(部分或全部值)

读:clientserverreqrespstorage

写:reqresp

返回值:

deliver 只交付 sysnthetic(string) 自定义内容,默认返回 sysnth 异常指定状态码与错误内容。

restart 重启请求。

 

vcl_init子程序:

加载vcl时最先调用,用于初始化VMODs,该子程序不参与请求处理,仅在vcl加载时调用一次。

可操作对象:(部分或全部值)

读:server

写:无

返回值:

ok 正常返回,进入vcl_recv子程序,默认返回值。

 

vcl_fini子程序:

卸载当前vcl配置时调用,用于清理VMODs,该子程序不参与请求处理,仅在vcl正常丢弃后调用。

可操作对象:(部分或全部值)

读:server

写:无

返回值:

ok 正常返回,本次vcl将释放,默认返回值。

varnish子程序调用流程图,通过大部分子程序的return返回值进入下一步行动:

wKiom1jU13PwQOkcAAT_6wtqCCM949.png

按照箭头颜色,箭头方向,分辨每个操作对象,进入的子程序

11优雅模式(Garce mode)

Varnish中的请求合并

当几个客户端请求同一个页面的时候,varnish只发送一个请求到后端服务器,然后让其他几个请求挂起并等待返回结果;获得结果后,其它请求再复制后端的结果发送给客户端;

但如果同时有数以千计的请求,那么这个等待队列将变得庞大,这将导致2类潜在问题:

惊群问题(thundering herd problem),即突然释放大量的线程去复制后端返回的结果,将导致负载急速上升;没有用户喜欢等待;

故为了解决这类问题,可以配置varnish在缓存对象因超时失效后再保留一段时间,以给那些等待的请求返回过去的文件内容(stale content),配置案例如下:

sub vcl_recv {

if (! req.backend.healthy) {

set req.grace = 5m;

} else {

set req.grace = 15s;

}

}

sub vcl_fetch {

set beresp.grace = 30m;

}

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

而根据后端主机的健康状况,varnish可向前端请求分别提供5分钟内或15秒内的过期内容