Varnish 是一款高性能且开源的反向代理服务器和 HTTP 加速器,其采用全新的软件体系机构,和如今的硬件体系紧密配合,与传统的squid 相比,varnish 具备性能更高、速度更快、管理更加方便等诸多优势,不少大型的网站都开始尝试使用 varnish 来替换 squid,这些都促进varnish 迅速发展起来。html
varnish主要运行两个进程:Management进程和Child进程(也叫Cache进程)。它们的工做原理大体以下图:web
Management进程主要实现应用新的配置、编译VCL、监控varnish、初始化varnish以及提供一个命令行接口等。Management进程会每隔几秒钟探测一下Child进程以判断其是否正常运行,若是在指定的时长内未获得Child进程的回应,Management将会重启此Child进程。 正则表达式
Child进程包含多种类型的线程,常见的如:Acceptor线程:接收新的链接请求并响应;Worker线程:child进程会为每一个会话启动一个worker线程,所以,在高并发的场景中可能会出现数百个worker线程甚至更多;Expiry线程:从缓存中清理过时内容; 编程
Varnish依赖“工做区(workspace)”以下降线程在申请或修改内存时出现竞争的可能性。在varnish内部有多种不一样的工做区,其中最关键的当属用于管理会话数据的session工做区。后端
为了与系统的其它部分进行交互,Child进程使用了能够经过文件系统接口进行访问的共享内存日志(shared memory log),所以,若是某线程须要记录信息,其仅须要持有一个锁,然后向共享内存中的某内存区域写入数据,再释放持有的锁便可。而为了减小竞争,每一个worker线程都使用了日志数据缓存。 浏览器
共享内存日志大小通常为90M,其分为两部分,前一部分为计数器,后半部分为客户端请求的数据。varnish提供了多个不一样的工具如varnishlog、varnishncsa或varnishstat等来分析共享内存日志中的信息并可以以指定的方式进行显示。缓存
varnish支持多种不一样类型的后端存储,这能够在varnishd启动时使用-s选项指定。后端存储的类型包括:bash
(1)file: 使用特定的文件存储所有的缓存数据,并经过操做系统的mmap()系统调用将整个缓存文件映射至内存区域(若是条件容许);服务器
(2)malloc: 使用malloc()库调用在varnish启动时向操做系统申请指定大小的内存空间以存储缓存对象,相似于C语言中的malloc动态申请函数;session
file和malloc存储方法相似,重启缓存服务时,先前缓存的数据不复存在。
说道varnish的状态引擎,不得不说vcl(Varnish Configuration Language:varnish配置缓存策略的工具)。它是基于域的一种简单的编程语言,支持算数运算、容许使用正则表达式、支持if语句等。使用vcl语言编写的缓存策略一般保存于.vcl文件中,其须要编译成二进制的格式后才能由varnish调用。
VCL用于让管理员定义缓存策略,而定义好的策略将由varnish的management进程分析、转换成C代码、编译成二进制程序并链接至child进程。varnish内部有几个所谓的状态(state),在这些状态上能够附加经过VCL定义的策略以完成相应的缓存处理机制,所以VCL也常常被称做“域专用”语言或状态引擎,“域专用”指的是有些数据仅出现于特定的状态中。
具体的状态的是经过定义内置函数来实现的,具体过程以下图:
vcl各状态引擎的功用:
vcl_recv:是在Varnish完成对请求报文的解码为基本数据结构后第一个要执行的子例程
vcl_fetch:根据服务器端的响应做出缓存决策
vcl_pipe:用于将请求直接发日后端主机;
vcl_hash:自定义hash生成时的数据来源
vcl_pass:用于将请求直接传递至后端主机;
vcl_hit:从缓存中查找到缓存对象时要执行的操做;
vcl_miss:从缓存中款查找到缓存对象时要执行的操做;
vcl_deliver:将用户请求的内容响应给客户端时用到的方法;
vcl_error:在varnish端合成错误响应而时;
缓存相关的HTTP首部:
HTTP协议提供了多个首部用以实现页面缓存及缓存失效的相关功能,这其中最经常使用的有:
(1)Expires:用于指定某web对象的过时日期/时间,一般为GMT格式;通常不该该将此设定的将来过
长的时间,一年的长度对大多场景来讲足矣;其经常使用于为纯静态内容如JavaScripts样式表或图片
指定缓存周期;
(2)Cache-Control:用于定义全部的缓存机制都必须遵循的缓存指示,这些指示是一些特定的指令,
包括public、private、no-cache(表示能够存储,但在从新验正其有效性以前不能用于响应客户端
请求)、no-store、max-age、s-maxage以及must-revalidate等;Cache-Control中设定的时间会覆
盖Expires中指定的时间;
(3)Etag:响应首部,用于在响应报文中为某web资源定义版本标识符;
(4)Last-Mofified:响应首部,用于回应客户端关于Last-Modified-Since或If-None-Match首部的请
求,以通知客户端其请求的web对象最近的修改时间;
(5)If-Modified-Since:条件式请求首部,若是在此首部指定的时间后其请求的web内容发生了更改,
则服务器响应更改后的内容,不然,则响应304(not modified);
(6)If-None-Match:条件式请求首部;web服务器为某web内容定义了Etag首部,客户端请求时能获取
并保存这个首部的值(即标签);然后在后续的请求中会经过If-None-Match首部附加其承认的标签列
表并让服务器端检验其原始内容是否有能够与此列表中的某标签匹配的标签;若是有,则响应304,
不然,则返回原始内容;
(7)Vary:响应首部,原始服务器根据请求来源的不一样响应的可能会有所不一样的首部,最经常使用的是
Vary: Accept-Encoding,用于通知缓存机制其内容看起来可能不一样于用户请求时
Accept-Encoding-header首部标识的编码格式;
(8)Age:缓存服务器能够发送的一个额外的响应首部,用于指定响应的有效期限;浏览器一般根据此
首部决定内容的缓存时长;若是响应报文首部还使用了max-age指令,那么缓存的有效时长为
“max-age减去Age”的结果;
# rpm包下载地址:https://repo.varnish-cache.org/
rpm -ivh varnish-3.0.5-1.el6.x86_64.rpm varnish-libs-3.0.5-1.el6.x86_64.rpm \
varnish-docs-3.0.5-1.el6.x86_64.rpm
修改varnish的相关参数:
## /etc/sysconfig/varnish 的有效参数以下:
NFILES=131072
MEMLOCK=82000
NPROCS="unlimited"
RELOAD_VCL=1
VARNISH_VCL_CONF=/etc/varnish/default.vcl
VARNISH_LISTEN_PORT=80 # 通常修改varnish的监听端口
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
VARNISH_ADMIN_LISTEN_PORT=6082
VARNISH_SECRET_FILE=/etc/varnish/secret
VARNISH_MIN_THREADS=50
VARNISH_MAX_THREADS=1000
VARNISH_THREAD_TIMEOUT=120
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin
VARNISH_STORAGE_SIZE=1G
VARNISH_STORAGE_MEM_SIZE=128M # 使用malloc存储方法时的内存大小
VARNISH_STORAGE="malloc,${VARNISH_STORAGE_MEM_SIZE}" # 配置后端存储方法为malloc
VARNISH_TTL=120
DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \
-f ${VARNISH_VCL_CONF} \
-T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \
-t ${VARNISH_TTL} \
-w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \
-u varnish -g varnish \
-S ${VARNISH_SECRET_FILE} \
-s ${VARNISH_STORAGE}"
配置vcl的相关参数:
# 修改 /etc/varnish/default.vcl 中如下内容:
backend default {
.host = "172.16.10.11";
.port = "80";
}
此时咱们访问:http://172.16.10.77时,会发现第一次响应速度比较慢,随后的响应是很快的。
此时,能够经过varnishstat查看缓存命中状况。
也能够自行定义缓存策略:
Varnish内置变量:
请求到达时可用的内置变量:
req.url
req.request
req.http.HEADER
req.restarts: 请求被重启的次数;
server.ip
server.port
server.hostname
client.ip
req.backend
向后后端主机请求时可用的内置变量
bereq.url
bereq.request
bereq.http.HEADER
bereq.connect_timeout
bereq.proto
从后端主机获取到响应的object时可用的内置变量
beresp.status
beresp.response
beresp.http.HEADER
beresp.ttl
beresp.backend.name
beresp.backend.ip
beresp.backend.port
缓存对象进入缓存时可用的内置变量(只能用于vcl_hit或vcl_error,且大多为只读)
obj.status
obj.response
obj.ttl
obj.hits
obj.http.HEADER
响应给客户端时可用的内置变量
resp.proto
resp.status
resp.response
resp.http.HEADER
自定义web.vcl,内容以下:
[root@varnish ~]# cat /etc/varnish/web.vcl
# 定义后端主机,并提供健康状态检测
backend web1 {
.host = "172.16.10.11";
.probe = {
.url="/index.html";
.interval=2s;
.window=8;
.threshold=2;
}
}
backend web2 {
.host = "172.16.10.16";
.probe = {
.url="/index.html";
.interval=2s;
.window=8;
.threshold=2;
}
}
director websrv round-robin {
{ .backend = web1; }
{ .backend = web2; }
}
# 定义一个acl 目的是进行缓存裁剪
acl purgers {
"127.0.0.1";
"172.16.0.0"/16;
}
sub vcl_recv {
set req.backend = websrv;
if (req.restarts == 0) {
if (req.http.x-forwarded-for) {
set req.http.X-Forwarded-For =
req.http.X-Forwarded-For + ", " + client.ip;
} else {
set req.http.X-Forwarded-For = client.ip;
}
}
# 定义那些内容不缓存的
if (req.url ~ "^/test.html$") {
return(pass);
}
if (req.request == "PURGE") {
if (!client.ip ~ purgers) {
error 405 "Method not allowed";
}
return (lookup);
}
if (req.request != "GET" &&
req.request != "HEAD" &&
req.request != "PUT" &&
req.request != "POST" &&
req.request != "TRACE" &&
req.request != "OPTIONS" &&
req.request != "DELETE") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
if (req.request != "GET" && req.request != "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 (lookup);
}
sub vcl_pipe {
return (pipe);
}
# sub vcl_hash {
# hash_data(req.url);
# if (req.http.host) {
# hash_data(req.http.host);
# } else {
# hash_data(server.ip);
# }
# return (hash);
# }
sub vcl_hit {
if (req.request == "PURGE") {
purge;
error 200 "Purged";
}
return (deliver);
}
sub vcl_miss {
if (req.request == "PURGE") {
purge;
error 404 "Not in cache";
}
return (fetch);
}
sub vcl_pass {
if (req.request == "PURGE") {
error 502 "PURGE on a passed object";
}
return (pass);
}
#
# sub vcl_fetch {
# if (beresp.ttl <= 0s ||
# beresp.http.Set-Cookie ||
# beresp.http.Vary == "*") {
# /*
# * Mark as "Hit-For-Pass" for the next 2 minutes
# */
# set beresp.ttl = 120 s;
# return (hit_for_pass);
# }
# return (deliver);
# }
sub vcl_deliver {
set resp.http.X-Age = resp.http.Age;
unset resp.http.Age;
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT Via" + " " + server.hostname;
} else {
set resp.http.X-Cache = "MISS Via" + " " + server.hostname;
}
return (deliver);
}
# sub vcl_deliver {
#
#
# return (deliver);
# }
#
# sub vcl_error {
# set obj.http.Content-Type = "text/html; charset=utf-8";
# set obj.http.Retry-After = "5";
# synthetic {"
# <?xml version="1.0" encoding="utf-8"?>
# <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
# "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
# <html>
# <head>
# <title>"} + obj.status + " " + obj.response + {"</title>
# </head>
# <body>
# <h1>Error "} + obj.status + " " + obj.response + {"</h1>
#"} + obj.response + {"# Guru Meditation:
#XID: "} + req.xid + {"# <hr>
#Varnish cache server# </body>
# </html>
# "};
# return (deliver);
# }
#
sub vcl_init {
return (ok);
}
sub vcl_fini {
return (ok);
}
vcl自动编译的方法,编辑 /etc/sysconfig/varnish 文件中的:
VARNISH_VCL_CONF=/etc/varnish/web.vcl