1、基本概念 2、varnish安装配置 3、VCL介绍 4、VCL自定义示例 5、varnish配置应用 ================================================================================================================================================ 基本概念 1、程序局部性原理 程序在执行时,呈现出局部性规律。 程序局部性原理表现为:时间局部性、空间局部性。 时间局部性:程序中的某条指令一旦为执行,则在不就之后很有可能再次被执行;如果某数据被访问,则很有可能再不久之后,此数据再次被访问。 空间局部性:指程序访问了某个存储单元的,则不久之后,程序很有可能访问此存储单元附近的位置。 2、缓存命中率 缓存命中率:hit/(hit+miss) (0,1) 页面命中率:基于页面数量进行衡量 字节命中率:基于页面的体积进行衡量 3、缓存对象 缓存对象有生命周期,过期缓存会被定期清理。缓存空间耗尽时,缓存程序会使用 LRU(最近最少使用算法) 算法清除过去缓存。缓存对象分为可 缓存和不可缓存的对象,例如不可缓存对象通常有用户私有数据。缓存对象的 key为 URL 的哈希值,值为 URL 所对应的数据。 4、缓存处理的步骤 接受请求 解析请求(提取请求URL及首部) 查询缓存 新鲜度检测 创建响应报文 发送响应 记录日志 5、新鲜度检测机制 过期机制 条件式请求 过期机制: 1、http/1.0 expires首部: 2、http/1.1 Cache-Control首部 3、有效性再验证:当客户端在使用缓存之前,向服务器发起询问,在某时间点之后,内容是否发生改变。 如果原始内容未改变,则仅响应首部,不附带body部分,。响应码为304 Not Modified 如果原始内容发生改变,则正常响应,响应码为200 如果原始内容消失,则响应码为404,此时缓存中的cache对象被删除 ETag 一个对象,比如URL的标志值,就一个对象而言,比如一个 html 文件,如果内容被修改了,其 Etag 也会别修改,所以ETag 的作用跟 Last-Modified 的作用差不多,主要供 WEB 服务器判断一个对象是否改变了。比如前一次请求某个 html 文件时,获得了其ETag,当 这次又请求这个文件时,浏览器就会把先前获得的 ETag 值发送给 WEB 服务器,然后 WEB 服务器会把这个 ETag 跟该文件的当前ETag 进行对比,然后就知道这个文件有没有改变了。 Expires 响应。WEB服务器表明该实体将在什么时候过期,对于过期了的对象,只有在跟WEB服务器验证了其有效性后,才能用来响应客户请求。是 HTTP/1.0的头部。例如:Expires:Sat, 23 May 2009 10:02:12 GMT 条件式请求: If-Match 如果对象的 ETag 没有改变,意味着对象没有改变,才执行请求的动作 If-None-Match 如果对象的 ETag 改变了,意味着对象也改变了,才执行请求的动作 If-Modified-Since 如果请求的对象在该头部指定的时间之后修改了,才执行请求的动作,比如返回对象,否则返回代码304,告诉浏览器该对象没有修改。 例如:If-Modified-Since:Thu, 10 Apr 2008 09:14:42 GMT If-Unmodified-Since 如果请求的对象在该头部指定的时间之后没修改过,才执行请求的动作(比如返回对象)。 Last-Modified 请求,WEB 服务器认为对象的最后修改时间,比如文件的最后修改时间,动态页面的最后产生时间等等。 例如:Last-Modified:Tue, 06 May 2008 02:42:43 GMT Cache-Cntrol 请求:no-cache:(不要缓存的实体,要求现在从WEB服务器去验证) no-store:必须请求原始内容 max-age:(只接受 Age 值小于 max-age 值,并且没有过期的对象) max-stale:(可以接受过期的对象,但是过期时间必须小于 max-stale 值) min-fresh:(接受其新鲜生命期大于其当前 Age 跟 min-fresh 值之和的缓存对象) 响应:public:(可以用 Cached 内容回应任何用户) private:(只能用缓存内容回应先前请求该内容的那个用户) s-maxage: 意思和 max-age 类似,但是只用于公有缓存,在公有缓存中使用的时候会覆盖 max-age 的值 no-cache:(可以缓存,但是只有在跟WEB服务器验证了其有效后,才能返回给客户端) max-age:(本响应包含的对象的过期时间) ALL:(no-store不允许缓存) only-if-cached: 该头域表示不进行与网络相关的交互,只返回已经缓存且满足要求的数据,否则的话返回 504 错误 6、常见的开源缓存服务解决方案 varnish squid nginx ================================================================================================================================================ varnish安装配置 1、安装 yum install varnish 2、jemalloc-3.6.0-1.el7.x86_64.rpm组件 能够更高效的完成内存分配 3、生成文件列表 /etc/varnish/default.vcl # 配置文件 /etc/varnish/varnish.params # varnish进程启动传递的参数,即varnish进程的工作属性 /run/varnish.pid /usr/bin/varnishadm # varnish命令行接口管理工具 /usr/bin/varnishhist /usr/bin/varnishlog # 记录日志的服务进程程序 /usr/bin/varnishncsa # 记录日志的服务进程程序 /usr/bin/varnishstat /usr/bin/varnishtest /usr/bin/varnishtop /usr/lib/systemd/system/varnish.service /usr/lib/systemd/system/varnishlog.service /usr/lib/systemd/system/varnishncsa.service /usr/sbin/varnish_reload_vcl # 重新加载配置文件 /usr/sbin/varnishd /var/lib/varnish /var/log/varnish /usr/share/doc/varnish-4.0.4/builtin.vcl # 内建的缓存策略 4、varnish存储缓存对象的方式 1、file 基于文件存储,将所有缓存都放置于一个文件中,不支持持久机制。重启服务,则保存在内存中的缓存元数据丢失。 2、malloc 基于内存存储,容易生成内存碎片 3、persistent 基于文件的持久存储 5、varnishd进程 -f VCL_NAME # 指定vcl文件 -a address[:port][,address[:port][...] # 指明监听地址和端口,可以监听多个地址和端口。默认监听端口是6081、6082 -d # Enables debugging mode -g GROUP_NAME # 指定以哪个组的身份运行程序 -g USER_NAME # 指定以哪个用户的身份运行程序 -s [name=]type[,options] # 指定缓存存储方式 · malloc[,size] # 内存存储 示例:"malloc,256M" · -s file,,, # "黑盒" 存放到文件系统上,临时有效。示例:"file,/data/cache/varnis/bin,2G" · persistent,path,size # 持久缓存。{varnish实验阶段} -T address[:port] # 在指定的地址和端口上提供管理界面 -p param=value # 设定额外的配置参数,可以运行时修改。也可以使用 varnishadm 的子命令 param.set thread_pools 4 thread_pools # 定义线程池个数,最好等于或小于CPU数量 thread_pool_max # 每个线程池最多线程 thread_pool_min # 每个线程池最少线程,最大空闲线程数 thread_pool_timeout # thread_pool_add_delay # 创建线程的延长时间 thread_pool_destroy_delay # -r PARAM_NAME ... # 设定可读参数列表 6、配置varnish 1、varnishd程序的命令行参数 /etc/varnish/varnish.params 配置监听的地址和端口 使用的存储类型 额外的配置参数 2、-p选项指明参数(运行时参数) -p param=carlue ... 3、vcl:配置缓存系统的缓存机制 /etc/varnish/default.vcl {需要先编译后应用,依赖于C编译器} 7、/etc/varnish/default.vcl vcl 4.0; backend default { # 后端服务器主机 .host = "127.0.0.1"; .port = "8080"; } sub vcl_recv { } sub vcl_backend_response { } sub vcl_deliver { } 8、简单配置测试 1、修改default.vcl文件 vcl 4.0; backend default { .host = "172.18.26.3"; .port = "8080"; } sub vcl_recv { } sub vcl_backend_response { } sub vcl_deliver { } 2、启动服务 systemctl start varnish.service 9、varnish相关的工具 1、varnishadm varnishadm [-n ident] [-t timeout] [-S secretfile] -T [address]:port command [...] -n is mutually exlusive with -S and -T 示例:varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 子命令 help # 帮助信息 200 help [] ping [] # 探测varnish auth quit banner status # 查看varnish进程状态。子进程是否处于正常状态 start # 启动子进程 stop # 停止子进程 vcl.load # 装载编译指定VCL文件为配置文件 vcl.inline vcl.use # 指明使用哪一个VCL文件 vcl.discard # 指明删除哪一个VCL文件 vcl.list # 列出所有可用的VCL文件。boot表示启动时加载的文件,数字表示文件编号 param.show [-l] [] # 显示运行时参数 param.set panic.show # 查看恐慌线程列表 panic.clear storage.list # 存储列表 vcl.show [-v] backend.list [] # 查看后端服务器列表 backend.set_health # 手动设定后端服务器的状态 ban [&& ]... ban.list 示例: vcl.load test default.vcl vcl.list vcl.show test vcl.use test vcl.list vcl.discard test param.show param.set thread_pools 4 param.show thread_pools 2、varnishlog 查看varnish共享内存中的日志信息,仅查看新生成的日志 3、varnishncsa 另一种风格的日志信息 4、varnishstat 查看varnish状态信息 varnishstat [-1] [-x] [-j] [-f field_list] [-l] [-n varnish_name] [-N filename] [-V] [-w delay] 示例: varnishstat -l # 查看选项 varnishstat -1 -f MAIN.cache_hit # 查看缓存命令的次数 varnishstat -1 -f MAIN.cache_hit # 查看缓存命令的次数 5、varnishtop 动态排序日志信息 选项: -1 # 一次性显示排序后的数据。默认为动态显示 -i # 显示指定标签的值的显示。多个标签值使用逗号 "," 隔开 -i # 显示指定标签的值的显示。多个标签值使用逗号 "," 隔开 -I # 显示指定标签的值的显示。多个标签值使用逗号 "," 隔开,支持正则表达式 -x # 排除指定标签的值的显示 -X # 排除指定标签的值的显示,支持正则表达式 示例: varnishtop -i RespHead # 只显示某个标签的动态变化 ================================================================================================================================================ VCL介绍 域 专有类型的配置语言。用来定义varnish缓存策略。 1、VCL state Engine 引擎之间存在相关性: vcl_pipe # 当varnish接收到自己无法理解的协议请求时,会将请求直接转发后端服务器 vcl_pass # 到后端服务器取资源 vcl_error # 当访问的资源没有权限或者错误时,varnish直接返回错误信息 vcl_hash # 自定义hash生成时的数据来源 vcl_hit # 从缓存中查找到缓存对象时要执行的操作 vcl_miss # 从缓存中款查找到缓存对象时要执行的操作 vcl_deliver # 将用户请求的内容响应给客户端时用到的方法 vcl_waiting # 并发请求过大,等待状态 vcl_purge # 清理缓存,此例程监控用户是否有清理缓存的请求,之后交给 vcl_synth 例程 2、VCL语法格式 1、注释 // # /* */ 2、定义子例程 sub vcl_recv { } 3、不支持循环 4、支持终止语句,return(action),没有返回值 5、操作符 = == ~ !&& || 6、条件判断 if (CONDTION) { } else { } 7、定义变量 set name=value unset name 8、取得请求报文中的首部 req.http.HEADER req.http.X-Forwarded-Forwarded-For 9、请求方法 req.request 10、return(action) 实现状态引擎的转换 3、VCL示例 *************************v3版本***************************** # 基于报文请求报头 sub vcl_recv { 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.request != "GET" && req.request != "HEAD" && req.request != "PUT" && req.request != "POST" && req.request != "TRACE" && req.request != "OPTIONS" && req.request != "DELETE") { return (pipe); } if (req.request != "GET" && req.request != "HEAD") { return (pass); } if (req.http.Authorization || req.http.Cookie) { return (pass); } return (lookup); } # 基于设备 sub vcl_recv { if (req.http.User-Agent ~ "iPad" || req.http.User-Agent ~ "iPhone" || req.http.User-Agent ~ "Android") { set req.http.X-Device = "mobile"; } else { set req.http.X-Device = "desktop"; } } # 基于URL sub vcl_recv { set req.http.x-host = req.http.host; set req.http.x-url = req.url; set req.http.host = regsub(req.http.host, "^www\.", ""); if (req.http.host == "sport.example.com") { set req.http.host = "example.com"; set req.url = regsub(req.url, "^", "/sport"); } if (req.http.host ~ "^sport\.") { set req.http.host = regsub(req.http.host,"^sport\.", ""); set req.url = regsub(req.url, "^", "/sport"); } } *************************v4版本***************************** sub vcl_recv { if (req.method == "PRI") { 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") { return (pipe); } if (req.method != "GET" && req.method != "HEAD") { return (pass); } if (req.http.Authorization || req.http.Cookie) { return (pass); } return (hash); } 4、varnish中的内置变量 https://varnish-cache.org/docs/4.1/reference/vcl.html# req # 客户端请求 req.backend_hint req.http.* # * 表示客户端请求的请求报文中的头部名称 req.http.User-Agent resp # 响应客户端 bereq # 发给后端服务器的请求 bereq.http.HEADERS # 发往后端服务器的请求报文的指定首部 bereq.request # 请求方法 bereq.url # 请求URL bereq.proto # 使用协议版本 bereq.backend # 选择调用后端的那一台服务器 beresp # 后端服务器响应 bereq.proto # 后端服务器响应的协议版本 bereq.status # 后端服务器状态 bereq.backend.ip # 后端服务器IP bereq.backend.name # 后端服务器主机名 beresp.do_gzip # 是否压缩后存入缓存 beresp.do_gunzip # 是否解压后存入缓存 beresp.http.HEADERS # 响应报文的首部 beresq.reason # 响应报文的原因短语 beresq.ttl # 后端服务器响应的内容的余下的生存时长。总共可缓存时长减去传输网络延时时长 client client.ip server server.ip server.hostname obj # 缓存对象相关 obj.hits # 缓存对象从缓存中命中的次数 obj.ttl # 缓存对象的ttl storage 5、varnish中的内置函数 https://varnish-cache.org/docs/4.1/reference/vcl.html# ban(expression) # 清理缓存对象 Invalidates all objects in cache that match the expression with the ban mechanism. hash_data(input) Adds an input to the hash input. In the built-in VCL hash_data() is called on the host and URL of the request. Available in vcl_hash. rollback() Restore req HTTP headers to their original state. This function is deprecated. Use std.rollback() instead. synthetic(STRING) Prepare a synthetic response body containing the STRING. Available in vcl_synth and vcl_backend_error. regsub(str, regex, sub) Returns a copy of str with the first occurrence of the regular expression regex replaced with sub. Within sub, \0 (which can also be spelled \&) is replaced with the entire matched string, and \n is replaced with the contents of subgroup n in the matched string. regsuball(str, regex, sub) As regsub() but this replaces all occurrences. ================================================================================================================================================ VCL自定义示例 1、自定义VCL # 复制default.conf脚本为test.conf sub vcl_recv { if (req.method == "PRI") { 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") { return (pipe); } if (req.method != "GET" && req.method != "HEAD") { return (pass); } if (req.http.Authorization || req.http.Cookie) { return (pass); } return (hash); } # 连接配置 varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 vcl.load test1 test.vcl # 加载编译vcl文件 vcl.list # 查看当前的vcl文件 vcl.show test1 # 查看vcl文件生效内容 2、自定义vcl的deliver动作 # 修改 test.vcl 文件 sub vcl_deliver { if (obj.hits>0) { set resp.http.X-cache = "HIT"; } else { set resp.http.X-cache = "MISS"; } } # 连接配置 vcl.load test1 test.vcl vcl.list vcl.use test1 vcl.list # 测试 后端服务器创建多个页面 for I in {1..10};do echo "

test${I}_varnish.node5.xuejinwei.com

" > test$I.html;done 使用客户端请求不同页面查看响应报头的内容 curl -I http://172.18.26.5/test1.html 3、示例 # 修改 test.vcl 文件 sub vcl_deliver { if (obj.hits>0) { set resp.http.X-cache = "HIT from " + server.ip; } else { set resp.http.X-cache = "MISS from " + server.ip; } } # 连接配置 vcl.load test1 test.vcl vcl.list vcl.use test1 vcl.list ================================================================================================================================================ varnish配置应用 官方示例:https://varnish-cache.org/trac/wiki/VCLExamples 1、支持后端web服务虚拟主机 2、对特定资源不允许使用缓存返回{使用URL匹配的方式} # 编辑vcl配置文件 sub vcl_recv { if (req.url ~ "^/test1.html$") { return(pass); } if (req.method == "PRI") { 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") { return (pipe); } if (req.method != "GET" && req.method != "HEAD") { return (pass); } if (req.http.Authorization || req.http.Cookie) { return (pass); } return (hash); } # 使用curl命令进行测试 curl -I 172.18.26.5/test1.html Note: 1、在对URL进行匹配时可以进行字符匹配且不区分大小写 if (req.url ~ "(?i)^/login" || req.url ~ "(?i)^/admin") { return(pass); } 3、对特定类型的资源,取消私有cookie标识 并设定资源在varnish中缓存的时长 {带有cookie信息的资源,varnish不会缓存} sub vcl_backend_response { if (beresp.http.cache-control !~ "s-maxage") { if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|css|js)$") { unset beresp.http.Set-Coolie; set beresp.ttl = 3600s; } if (bereq.url ~ "(?i)\.css$") { set beresp.ttl = 300s; unset beresp.http.Set-Coolie; } } } 4、判定重新请求 sub vcl_recv { if (req.restarts == 0) { 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、缓存对象修改 purge sub vcl_recv { if (req.method == "PURGE") { return(purge); } } sub vcl_purge { return (synth(200,"Purged")); } 测试手动使得缓存失效: curl -X PURGE http://172.18.26.1/1.png ban 添加acl规则,使得只能有指定客户端可以执行PURGE请求: acl purgers { "127.0.0.0"/8; "10.1.0.0"/16; } sub vcl_recv { if (req.method == "PURGE") { if (!client.ip ~ purgers) { return(synth(405,"Purging not allowed for " + client.ip)); } return(purge); } } 6、缓存对象修改 ban 命令行管理: 格式:ban 示例:ban req.url ~ (?i).(jpg|jpeg)$ 配置文件管理: if (req.method == "BAN") { ban("req.http.host == " + req.http.host + " && req.url == " + req.url); return(synth(200, "Ban added")); } ban req.http.host==www.ilinux.io && req.url==/test1.html 7、实现动静分离,使得缓存服务器从不同的后端服务器取的资源 # test.vcl配置 vcl 4.0; backend web1 { .host = "172.18.26.6"; .port = "80"; .probe = { .url = "/heal.html"; } } backend web2 { .host = "172.18.26.7"; .port = "80"; .probe = { .url = "/heal.html"; } } sub vcl_recv { if (req.url ~ "(?i)\.(jpg|png|gif)$") { set req.backend_hint = web1; } else { set req.backend_hint = web2; } } Note: 查看后端服务器状态信息 backend.list [] backend.set_health 8、配置多个后端主机实现负载均衡效果 后端主机示例配置: vcl 4.0; backend b1 { .host = "..."; .port = "..."; } backend b2 { .host = "..."; .port = "..."; .probe = healthcheck; # 是否对后端主机进行健康状态检测 .max_connections N; # 并发连接最大请求数 .connect_timeout = 0.5s; # 连接超时时长 .first_byte_timeout = 20s; # 第一个字节传输超时时长 .between_bytes_timeout = 5s; # 两个字节之间传输的超时时长 .max_connections = 50; # 最大连接数 } sub vcl_init { # vcl状态初始化 new cluster1 = directors.round_robin(); # 定义一个新的集群,使用round_robin()调度方式 cluster1.add_backend(b1, 1.0); # 给集群添加后端主机,b1是名称,1.0是权重 cluster1.add_backend(b2, 1.0); } sub vcl_recv { set req.backend_hint = cluster1.backend(); # 调用cluster1集群 } 健康状态检测配置: probe healthcheck { # healthcheck为检测名称 {定义healthcheck} .url = "/"; # 定义健康检查的页面,表示对那个页面进行检测 .interval = 6s; # 探测请求的发送周期,默认为5秒; .expected_response = 200; # 期望检测对检测页面请求时的返回状态码,默认为200 .timeout = 0.3s; # 每次探测请求的过期时长 .window = 8; # 设定在判定后端主机健康状态时基于最近多少次的探测进行 .threshold = 3; # 在.window中指定的次数中,至少有多少次是成功的才判定后端主机正在健康运行 .initial = 3; # Varnish启动时对后端主机至少需要多少次的成功探测,默认同.threshold; } 手动设定后端主机的状态: backend.set_health BACKEND_NAME sick # 标记为维护模式 backend.set_health BACKEND_NAME auto # 由探测结果决定后端主机是否健康 backend.set_health BACKEND_NAME heakthy # 直接标记为健康状态 负载均衡算法: fallback random hash round_robin 实验配置: vcl 4.0; backend web1 { .host = "172.18.26.6"; .port = "80"; .probe = { .url = "/heal.html"; } } backend web2 { .host = "172.18.26.7"; .port = "80"; .probe = { .url = "/heal.html"; } } import directors; sub vcl_init { new webcluster = directors.round_robin(); # 对资源进行轮询 webcluster.add_backend(web1); webcluster.add_backend(web2); } sub vcl_recv { set req.backend_hint = webcluster.backend(); if (req.url ~ "(?i)^/test1\.html$") { # 为了显示实验效果,故意不将test1.html页面缓存 return(pass); } } 9、基于cookie的session sticky sub vcl_init { new h = directors.hash(); h.add_backend(one, 1); // backend 'one' with weight '1' h.add_backend(two, 1); // backend 'two' with weight '1' } sub vcl_recv { // pick a backend based on the cookie header of the client set req.backend_hint = h.backend(req.http.cookie); } ================================================================================================================================================ 博客作业:以上所有内容; 实战项目:两个lamp部署wordpress,用Nginx反代,做压测;nginx后部署varnish缓存,调整vcl,多次压测;