Varnish 负载均衡
Changlogs
2011-06-20 添加测试报告, 在另一台服务器上测试,1W并发,首页index.php无Cache,512M内存的VPS能达到这个程度也算不错.并发几千还是能承载的.就是没几个人来访问,哎^_*!
1 | [root@localhost webbench-1.5]# ./webbench -c 10000 -t 30 http://www.hezhiqiang.info/index.php Webbench - Simple Web Benchmark 1.5 Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software. Benchmarking: GET http://www.hezhiqiang.info/index.php 10000 clients, running 30 sec. Speed=21030 pages/min, 3073918 bytes/sec. Requests: 8441 susceed, 2074 failed. |
Varnish 有内置的负载均衡支持. 如果有多个源服务器,使用起来非常简易.通过声明多个后端服务器启动Varnish,同时设置后端服务器的probe属性来监控后端服务器的健康状况.
1 | /* 如果VCL定义文件内容太多可以分离到单独的文件,并包含进来 */ #include "backends.vcl"; #include "ban.vcl"; backend default { .host = "127.0.0.1"; .port = "8080"; /* Varnish 到后端服务器的连接超时, */ .connect_timeout = 1s; /* 和后端服务器建立连接后,即进入等待接收响应状态,如果响应的第一个字节在5s内没有到达,被认为超时*/ .first_byte_timeout = 5s; /* 第一个字节到达后, 如果后续字节在2s内没有到达,被认为是超时 */ .between_bytes_timeout = 2s; .probe = { .url = "/"; .interval = 5s; .timeout = 1 s; .window = 5; .threshold = 3; } } backend default8081 { .host = "127.0.0.1"; .port = "8081"; .connect_timeout = 1s; .first_byte_timeout = 5s; .between_bytes_timeout = 2s; .probe = { .url = "/"; .interval = 5s; .timeout = 1 s; .window = 5; .threshold = 3; } } |
有了两个后端服务器定义, default 和 default8081 . probe对象指出,Varnish应该每隔5秒去获取一次 / 的内容.如果超过一秒还没有得到内容,被认为是失败.如果五次获取有三次成功,那么认为后端服务器状态被认为是健康. 健康状态检查的详细信息请查看 backend health polling
probe 在Varnish源码中定义为一个C Struct:
1 | struct vrt_backend_probe { const char *url; const char *request; double timeout; double interval; unsigned exp_status; unsigned window; unsigned threshold; unsigned initial; }; |
Director 是一个或多个后端服务器的逻辑组, 在启用健康检查的情况下,当有后端服务器不可用时, 它会自动切换到另一个后端服务器上去. 这在需要系统宕机维护, 同时又不能中断服务的情况下是非常有用的, 比如,凌晨2:00,系统维护,可以关闭一台后端服务器,维护完成后再启动,接着维护下一台,以此类推.
现在这两个后端需要被包含在一个逻辑的 director 中.被作为一个虚拟的后端,称之为 hezhiqiang:
1 | directory hezhiqiang round-robin { {.backend = default;} {.backend = default8081;} } |
关键字 round-robin 指出,请求会以轮询的方式分布到后端服务器.当前(varnish 3.0)还支持另外的director, 包括 simple, hash, random, dns, client.
这些支持的后端负载均衡方式可以在源代码 cache_backend_cfg.c 文件中看到.
1 | void VRT_init_dir(struct cli *cli, struct director **dir, const char *name, int idx, const void *priv) { ASSERT_CLI(); if (!strcmp(name, "simple")) VRT_init_dir_simple(cli, dir, idx, priv); else if (!strcmp(name, "hash")) VRT_init_dir_hash(cli, dir, idx, priv); else if (!strcmp(name, "random")) VRT_init_dir_random(cli, dir, idx, priv); else if (!strcmp(name, "dns")) VRT_init_dir_dns(cli, dir, idx, priv); else if (!strcmp(name, "round-robin")) VRT_init_dir_round_robin(cli, dir, idx, priv); else if (!strcmp(name, "client")) VRT_init_dir_client(cli, dir, idx, priv); else INCOMPL(); } |
现在可以把特定的请求定向到后端服务器了. 在 vcl_recv , 如下:
1 | sub vcl_recv { # 把后端服务器指向上面定义的 directory set req.backend = hezhiqiang; } |
Client director
Client director 在 Vanish 2.1.3以及之后版本可用.
如果基于客户端提供的信息做负载均衡,比如IP地址, HTTP 头, 或则请求URL, 那么就可以使用client director
1 | sub vcl_recv { /* Set which backend will be used */ set req.backend = hezhiqiang; /* Load balance by user agent */ set client.identity = req.url; /* Load balance by user agent */ #set client.identity = client.ip; /* Load balance by user agent */ #set client.identity = req.http.user-agent; } |
DNS Director
指定384个后端服务器,其中全部在80端口监听, 0.4秒超时.
1 | director directorname dns { .list = { .host_header = "www.example.com"; .port = "80"; .connect_timeout = 0.4; /* 使用掩码标识的网段作为后端 */ "192.168.15.0"/24; "192.168.16.128"/25; } /* 指定DNS查询的缓存时间 */ .ttl = 5m; /* 追加到客户端提供的主机头后面 */ .suffix = "internal.example.net"; } |
一个完整的配置
采用round-robin双Nginx后端.搭建在Linode的Plan512 VPS节点上,同时采用XCache作为PHP的加速器,Percona MySQL 服务器.
1 | /*import std;*/ backend default { .host = "127.0.0.1"; .port = "8080"; /* Varnish 到后端服务器的连接超时, */ .connect_timeout = 1s; /* 和后端服务器建立连接后,即进入等待接收响应状态,如果响应的第一个字节在5s内没有到达,被认为超时*/ .first_byte_timeout = 5s; /* 第一个字节到达后, 如果后续字节在2s内没有到达,被认为是超时 */ .between_bytes_timeout = 2s; .probe = { /* 健康检查的目标URL */ .url = "/index.html"; /* 每次检查间隔 */ .interval = 5s; /* 超时 */ .timeout = 1 s; /* 检查次数 */ .window = 5; /* 成功次数 */ .threshold = 3; } } backend default8081 { .host = "127.0.0.1"; .port = "8081"; .connect_timeout = 1s; .first_byte_timeout = 5s; .between_bytes_timeout = 2s; .probe = { .url = "/"; .interval = 5s; .timeout = 1 s; .window = 5; .threshold = 3; } } director hezhiqiang round-robin { {.backend = default;} {.backend = default8081;} } sub vcl_recv { set req.backend = hezhiqiang; # Load balance by url set client.identity = req.url; # Load balance by client ip #set client.identity = client.ip; # Load balance by user agent #set client.identity = req.http.user-agent; # std.log("fishy is going on with the vhost" + req.http.host); if (req.request == "GET" && req.url ~ "\.(gif|jpg|png|swf|css|js)$") { unset req.http.Cookie; } # do not cache the /tag, /textpattern, and /category if ( req.url ~ "^/tag/" ) { return (pass); } if ( req.url ~ "^/textpattern/") { return (pass); } if ( req.url ~ "^/category/" ) { return (pass); } if ( req.url ~ "^/html/") { return (pass); } } sub vcl_deliver { # add debugging headers, so we can see what's cached set resp.http.X-Served-By = server.hostname; if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; set resp.http.X-Cache-Hits = obj.hits; } else { set resp.http.X-Cache = "MISS"; } # remove some headers added by varnish # unset resp.http.Via; # unset resp.http.X-Varnish; } |