Varnish 进阶与优化(一) weir 2015-08-17 17:13:55.0 varnish,分布式 2229 上一次谈论varnish时没有提到squid ,今天突然查了一下看来squid也老了,所以还是算了不去了解了。先看看varnish的优势虽然之前也多次强调过我们用它就是发挥那对内存的优秀管理和利用能力: 主要基于内存或者是虚拟内存进行缓存,性能好 支持设置精确的缓存时间 VCL配置管理比较灵活 后端服务器的负载均衡和健康检查 局部支持ESI URL地址重写 优雅的处理后端服务器宕机的问题 32位机器上缓存文件大小为最大2GB 对计算机内存的管理说实话是相当的麻烦,好在我们有linux 为什么,最起码它是开源的吧,那些linux的大神完全可以利用好linux的内存并让其发挥到极致,在这点上应该是没有问题的,所以如果你追求高性能的处理能力,操作系统尤其重要。 首先你要清楚的是varnish是基于配置的,有专门的配置语言,配置的好与坏决定varnish能发挥多大作用,今天我们从安装运行开始。 我的测试环境是centos 当时是6.5 我想更高的或者第一点也没问题,官网有一些文档还是可以参考一下,虽然英文不咋地也没事:https://www.varnish-cache.org/docs/4.0/installation/index.html 比如这段话: Build dependencies on Red Hat / CentOS To build Varnish on a Red Hat or CentOS system you need the following packages installed: autoconf automake jemalloc-devel libedit-devel libtool ncurses-devel pcre-devel pkgconfig python-docutils python-sphinx 这还是可以看懂的(偷笑),在编译安装之前对系统的要求,需要加一下依赖的东西,很多linux系统下的软件都是这样。 你可以一股脑的这样: yum install autoconf automake jemalloc-devel libedit-devel libtool ncurses-devel pcre-devel pkgconfig python-docutils python-sphinx 全部安装了。 你可以下载最新的版本4.0.3了吧,解压之后进入根目录配置一下安装路径: ./configure --prefix=/usr/common/varnish 然后就可以make && make install 一些安装的注意事项 想什么防火墙呀 权限呀 我就不啰嗦了。 安装完成之后最重要的还是配置文件default.vcl 这是默认的当然你可以自己取了。 # This is a basic VCL configuration file for varnish. See the vcl(7) # man page for details on VCL syntax and semantics. # # Default backend definition. Set this to point to your content # server. # import std; backend b1 { .host = "127.0.0.1"; .port = "8080"; ##等待连接后端的时间 .connect_timeout = 1s; ##等待从backend传输过来的第一个字符的时间 .first_byte_timeout = 5s; ##两个字符的间隔时间 .between_bytes_timeout = 2s; .probe = { .url = "/index.jsp"; .timeout = 0.3 s; .window = 8; //要检查后端服务器的次数 .threshold = 3; //.window里面要有多少polls成功就认为后端是健康的 .initial = 3; //当varnish启动的时候,要确保多少个probe正常 } } probe p1 { .url = "/index.jsp"; .timeout = 0.3 s; .window = 8; //要检查后端服务器的次数 .threshold = 3; //.window里面要有多少polls成功就认为后端是健康的 .initial = 3; //当varnish启动的时候,要确保多少个probe正常 } backend b2 { .host = "127.0.0.1"; .port = "9080"; .connect_timeout = 1s; .first_byte_timeout = 5s; .between_bytes_timeout = 2s; ##可以设置连接后端服务器得最大限制数 .max_connections=1000; .probe = p1; } director d1 fallback { { .backend = b1;//引用已经存在的backend } { .backend = b2; } } acl purgeallow { "127.0.0.1"; #"192.168.2.114"; } sub vcl_recv { set req.backend=d1; if(!req.backend.healthy){ set req.grace = 30m; }else{ set req.grace = 5s; } if(req.request == "PURGE") { if(!client.ip ~ purgeallow) { error 405 "not allowed."; } return(lookup); } if (req.request == "GET" && req.url ~ "\.(jpg|png|gif|swf|flv|ico|jpeg)$") { unset req.http.cookie; } if (req.request =="GET" && req.url ~ "(?i)\.jsp($|\?)"){ return (pass); } 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") { /* 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 { # # Note that only the first request to the backend will have # # X-Forwarded-For set. If you use X-Forwarded-For and want to # # have it set for all requests, make sure to have: # # set bereq.http.connection = "close"; # # here. It is not set by default as it might break some broken web # # applications, like IIS with NTLM authentication. return (pipe); } # sub vcl_pass { return (pass); } # 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 { return (deliver); } # sub vcl_miss { std.log("now miss url===" + req.url); return (fetch); } # sub vcl_fetch { set beresp.grace = 30m; if(beresp.status == 500){ set beresp.saintmode = 20s; return(restart); } 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); } if (beresp.http.Pragma ~ "no-cache" || beresp.http.Cache-Control ~ "no-cache" || beresp.http.Cache-Control ~ "private") { return(deliver); } if(beresp.status == 404 || beresp.status == 300) { error 404; } if (req.request == "GET" && req.url ~ "\.(jpg|png|gif|swf|flv|ico|jpeg)$") { set beresp.ttl = 1m; } if (req.request == "GET" && req.url ~ "\.(htm|html)$") { set beresp.ttl = 1d; } # if (req.url ~ "\.(png|gif|jpg)$") { # unset beresp.http.set-cookie; # set beresp.ttl = 1h; # } return (deliver); } # sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "cached"; } else { set resp.http.x-Cache = "uncached"; } # Remove some headers: PHP version unset resp.http.X-Powered-By; # Remove some headers: Apache version & OS unset resp.http.Server; 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> ccccccccccccccccccccccccc<br> <head> <title>"} + obj.status + " " + obj.response + {"</title> </head> <body> <h1>Error "} + obj.status + " " + obj.response + {"</h1> <p>"} + obj.response + {"</p> <h3>Guru Meditation:</h3> <p>XID: "} + req.xid + {"</p> <hr> <p>Varnish cache server</p> </body> </html> "}; return (deliver); } sub vcl_init { return (ok); } # sub vcl_fini { return (ok); } # # Below is a commented-out copy of the default VCL logic. If you # redefine any of these subroutines, the built-in logic will be # appended to your code. # 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") { # /* 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 { # # Note that only the first request to the backend will have # # X-Forwarded-For set. If you use X-Forwarded-For and want to # # have it set for all requests, make sure to have: # # set bereq.http.connection = "close"; # # here. It is not set by default as it might break some broken web # # applications, like IIS with NTLM authentication. # return (pipe); # } # # sub vcl_pass { # return (pass); # } # # 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 { # return (deliver); # } # # sub vcl_miss { # return (fetch); # } # # 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 { # 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> # <p>"} + obj.response + {"</p> # <h3>Guru Meditation:</h3> # <p>XID: "} + req.xid + {"</p> # <hr> # <p>Varnish cache server</p> # </body> # </html> # "}; # return (deliver); # } # # sub vcl_init { # return (ok); # } # # sub vcl_fini { # return (ok); # } 运行命令 ./varnishd -f /usr/common/varnish/etc/varnish/default.vcl -s malloc,32M –T 127.0.0.1:2000 -a 0.0.0.0:1111 其中:1:-f 指定要运行的配置文件 2: -s malloc,32M :–s 选项用来确定varnish使用的存储类型和存储容量,这里使用 的是malloc类型(malloc是一个C函数,用于分配内存空间) 3:-T 127.0.0.1:2000 : 指定varnish的管理ip和端口 4: -a 0.0.0.0:1111 :指定varnish对外提供web服务的ip和端口 这就是高性能付出的代价,什么都要配置,一点都不方便也。 你要是直接复制粘贴上面的配置然后运行那就不行的啦(别介意我发一下下骚哦) 关闭varnish 也是很暴力的哦 到valish/sbin的路径下,运行pkill varnished 我们还是先介绍一下varnish 的配置语法也好知道我们怎么去写自己的配置文件。 VCL(Varnish Configuration Language):Varnish配置语言,语法简单,功能强 大,类似于c、perl。主要用来配置如何处理请求和内容的缓存策略。 VCL在执行时,会转换成二进制代码。 VCL文件被分为多个子程序,不同的子程序在不同的时间里执行,比如一个子程序在 接到请求时执行,另一个子程序在接收到后端服务器传送的文件时执行。 基本语法介绍 1:用花括号做界定符,使用分号表示声明结束。注释用//、#、/* */ 2:赋值(=)、比较(==)、和一些布尔值(!、&&、||),!(取反)等类似c语法 3:支持正则表达式,ACL匹配使用~ 操作 4:不同于C的地方,反斜杠(\)在VCL中没有特殊的含义。只是用来匹配URLs 5:VCL没有用户定义的变量,只能给backend、request、document这些对象的变量赋值, 大部分是手工输入的,而且给这些变量分配值的时候,必须有一个VCL兼容的单位 6:VCL有if,但是没有循环。 7:可以使用set来给request的header添加值,unset 或remove 来删除某个header 申明backend 一个backend申明创建和初始化一个backend目标: backend sishuok { .host = "www.sishuok.com"; .port = “8080"; } 一个请求可以选择一个Backend: if (req.http.host ~ "^(www.)?sishuok.com$") { set req.backend = sishuok; } 还可以给它设置很多的参数,如下所示: backend sishuok { .host = "www.sishuok.com"; .port = “8080"; .connect_timeout = 1s; .first_byte_timeout = 5s; .between_bytes_timeout = 2s; .max_connections=1000; } 为了避免后端服务器过载,.max_connections 可以设置连接后端服务器得 最大限制数。 在backend中申明的timeout参数可以被覆盖,.connect_timeout 等待连接 后端的时间;.first_byte_timeout 等待从backend传输过来的第一个字符的时 间;.between_bytes_timeout 两个字符的间隔时间。 Director:是backend的逻辑分组或backend的集群。主要有随机、循环和DNS几种Director,不同类型 的Director具有不同的算法来选择backend。比如随机的Director示例如下: director b2 random { .retries = 5; { .backend = b1;//引用已经存在的backend .weight = 7; } { .backend = { //或者是直接在这里定义backend .host = "fs2"; } .weight = 3; } } .retries这个参数指定查找可用后端的次数。默认director中的所有后端的.retries相同。 .weight表示这个后端的权重 随机的Director又分成三种,分别是:random、client、hash,他们采用同样的 随机分发算法,只是种子数值不同,种子数分别采用随机数、客户端id,或者是 缓存的hash(典型如url)。 对于client director 你可以通过设置VCL的变量client.identity来区分客户端,值可以从 session cookie 或其它相似的值来获取 对于hash director 默认使用URL的hash值,可以通过req.hash 获取到 round-robin director 它没有什么选项,就是一次循环使用backend,第一个请求用第一个 backend,第二个请求用第二个,以此类推。 如果某个backend出了问题,它会继续尝试下一个,理论上它要尝试完所有 的backend,都不好用的话,才会出错。 DNS director有两种不同的方法来选择后端,一种是random或者round-robin;另一种是使 用.list(list的方式不支持ipv6): director directorname dns { .list = { .host_header = "www.example.com"; .port = "80"; .connection_timeout = 0.4; "192.168.15.0"/24; "192.168.16.128"/25; } .ttl = 5m; .suffix = "internal.example.net"; } 这段代码会制定384个后端,都使用80端口及0.4s的连接超时,.list声明中设置选 项必须在IPS的前面。.ttl定义DNSlookups的时间。 fallback director:选择第一个健康的backend,示例: director b3 fallback { { .backend = www1; } { .backend = www2; } // 第一个不好用,才会到这里 { .backend = www3; } // 前两个都不好用,才会到这里} probe(后端探针):探测后端,确定他们是否健康,返回的状态用req.backend.healthy核对: backend sishuok { .host = "www.sishuok.com"; .port = “8080"; .probe = { .url = "/test.jpg"; .timeout = 0.3 s; .window = 8; //要检查后端服务器的次数 .threshold = 3; //.window里面要有多少polls成功就认为后端是健康的 .initial = 3; //当varnish启动的时候,要确保多少个probe正常 } } 当然,也可以把probe从backend中拿出来单独定义,形如: backend sishuok{…… .probe=p1; } probe p1{……} 可能用到的参数: .url:访问backend的路径,缺省是”/” .request:设置详细的请求头,是一些字符串 .window:要检查后端服务器的次数,默认是8 .threshold:.window里面要有多少polls成功就认为后端是健康的,默认是3 .initial:当varnish启动的时候,要确保多少个probe正常,默认和threshold一样 .expected_response:期望的response code,默认是200 .interval:定义probe多久检查一次后端,默认是5秒 .timeout:定义probe的过期时间,默认是2秒 也可以指定原始的http请求,形如: backend sishuok { .host = "www.sishuok.com"; .port = “8080"; .probe = { .request ="GET / HTTP/1.1“ "Host: www.foo.bar“ "Connection: close"; }} ACLs :访问控制列表,示例如下: acl local { "localhost"; "192.0.2.0"/24; ! "192.0.2.23"; } 如果一个ACL中指定一个主机名,varnish不能解析,他将解析匹配到所有地址。 如果你使用了一个否定标记(!),那么将拒绝匹配所有主机。 下面是一个匹配的示例: if (client.ip ~ local) { pipe; } varnish的配置信息还是很多的,今天先写到这里,这些东西需要在实际工作中反复的去看去理解。