[民工日记]简单服务器GEO-Routing: 根据ip自动分流

不通过应用程序而纯粹从服务器简单配置。。。transparent to your apps

1. 用 apache mod_rewrite 实现根据用户ip自动选择镜像

这是一个非常简单的实现–如果用户来自中国,自动重定向到位于中国的服务器,否则转到海外的服务器。并且自动检测服务器状态,如果一个服务器倒了所有traffic都会转到另外的server.

在 apache config 里加一个 virtual host:

ServerAdmin [email protected]
DocumentRoot /hosting/balance
ServerName balance
<filesmatch "\.(php|htm|html|pl|asp)"="">
Allow from all

RewriteEngine on
RewriteLog rewrite.log
RewriteLogLevel 9
RewriteMap lb prg:/hosting/balance/ip_prg.php
RewriteRule ^(.*)$ ${lb:$1} [P]
CustomLog "logs/balance_access.log" combined
ErrorLog "logs/balance_error.log"

then I have a simple php script ip_prg.php (as configured in apache shown above) to do the redirection:

while( $url = trim(fgets(STDIN)))
{

$protocol="http";
if (strtolower($_SERVER["HTTPS"]) == "on")
{
$protocol="https";
}
$status_www = intval(file_get_contents("/2way/mirror1"));
$status_www1 = intval(file_get_contents("/2way/mirror2"));

if ($status_www == 0 || $status_www1 == 0){
if (isFromChina() && trim($status_www) == '0'){
$server = "mirror1.domain.com";
}else if (! isFromChina() && trim($status_www1) == '0'){
$server = "mirror2.domain.com";
}else if (! isFromChina() && trim($status_www) == '0'){
$server = "mirror1.domain.com";
}else if (isFromChina() && trim($status_www1) == '0'){
$server = "mirror2.domain.com";
}
}else {
//site not avilable.. redirect to some notice or matenace pagehere
...
}

$address = $protocol."://".$server.($_SERVER["REQUEST_URI"] != '' ? $_SERVER["REQUEST_URI"] : '');
if(fwrite(STDOUT, $address."\n" ) === FALSE)
{
log2( "can't write fff");
}
}

重定向的好处是随后的traffic不再经过load balance server.

视乎需要,也可以用 mod_proxy 来proxy request达到对用户透明,而不用显式重定向。

但是如果用proxy的话,专门的reverse proxy 软件更容易。 下面说说用varnishd 来实现类似功能:

2. 用varnishd proxy 作load balance或者根据ip分配流量

以前简单介绍过varnishd 这个reverse proxy. 比较起squid之类的老软件来, varnish一是在性能上有不少提高,而且非常灵活–配置是通过vcl语言编写的script而非传统的配置文件。

简单vcl进行round robin load balance 的例子:

/* defined backends */
backend mirror1 {
.host = "mirror1.example.com";
.probe = {
.url = "/";
.interval = 5s;
.timeout = 1 s;
.window = 5;
.threshold = 3;
}
}

backend mirror2 {
.host = "mirror2.example.com";
.probe = {
.url = "/";
.interval = 5s;
.timeout = 1 s;
.window = 5;
.threshold = 3;
}
}

sub vcl_recv {
remove req.http.X-Forwarded-For;
set req.http.X-Forwarded-For = client.ip;

if (req.requst.host ~ "^(www.)?domain.com") {
set req.backend = bal;
}
if (req.request == "GET" && req.url ~ "\.(jpg|jpeg|gif|ico|html|htm|css|js|pdf|xls|vsd|doc|ppt|iso)$") {
lookup;
}
if (req.http.Expect) {
pipe;
}
if (req.http.Cache-Control ~ "no-cache") {
pass;
}
....

用client ip来决定的话, 需要安装一个geoip varnish plugin ( http://github.com/cosimo/varnish-geoip ), 然后vcl可以改为:

include "/etc/varnish/geoip.vcl";
/* define backends mirror1 and mirror2 */
...
/* now the vcl_recv() callback */
sub vcl_recv {
remove req.http.X-Forwarded-For;
set req.http.X-Forwarded-For = client.ip;

C{
vcl_geoip_set_header(sp);
}C

if (req.http.host ~ "^(www.)?domain.com$") {
if (req.http.X-Geo-IP ~ "China") {
set req.backend = mirror1;
}else {
set req.backend = mirror2;
}
}

if (req.request == "GET" && req.url ~ "\.(jpg|jpeg|gif|ico|html|htm|css|js|pdf|xls|vsd|doc|ppt|iso)$") {
lookup;
}
if (req.http.Expect) {
pipe;
}
if (req.http.Cache-Control ~ "no-cache") {
pass;
}
....

最后一段代码大意如是, 没测试过。。。嗯, 懒:)

About: mmpower

Software Architect & Soccer Fun 黑超白袜 = IT 民工 + 摇滚大叔


Leave a Reply

Your email address will not be published.