正在浏览 Web服务 里的文章

简述:利用(LVS+Piranha)基于完整开源软件的架构可以提供一个简单的负载均衡及高可用的服务架构。LVS 集群采用 IP 负载均衡技术和基于内容请求分发技术。调度器具有很好的吞吐率(在DR模式下),将请求均衡地转移到不同的服务器上执行,且调度器自动屏蔽掉服务器的故 障,从而将一组服务器构成一个高性能的、高可用的虚拟服务器。

生产环境中使用效果图:

环境:CentOS 5.5  x64

lb01:  192.168.0.111
lb02:  192.168.0.112
VIP:   192.168.0.115
web01: 192.168.0.114
web02: 192.168.0.115

安装piranha:

yum -y install piranha ipvsadm

#通过piranha-passwd设置Piranha的管理员密码

piranha-passwd  

#启动piranha的WEB管理界面(可选)

/etc/init.d/piranha-gui start  

通过浏览器访问http://192.168.0.111:3636,单击"Login",使用用户名piranha及刚刚设定的密码进行登录

注:在此界面下可以图形化配置,也可不启用此WEB界面,直接编辑配置文件来配置

vi /etc/sysconfig/ha/lvs.cf

继续阅读

假设我这里有大量图像、CSS、javascript等静态文件,分别放在后端服务器  192.168.1.5 和 192.168.1.6上,那么我如何利用nginx的反向代理功能将不同的 http_user_agent 请求发送到指定的服务器上呢?如 "Mozilla" 转发到 192.168.1.5 ,MSIE  转发到 192.168.1.6 。

Nginx web 服务器支持if条件表达式,由此来跳转或者使用不同的配置变量。在本文中需要使用 $http_user_agent 变量,它标记了用户浏览器的类别,版本以及操作系统的一些信息,语法如下:

if ( condition ){
  do_something
}
if ( $http_user_agent = "wget" ){
   do_something
}
if ( $http_user_agent ~ MSIE ){
   return 403;
}

if指令会就检查后面表达式的值是否为真(true),如果为真,则执行后面大括号中的内容。以下是一些条件表达式的比较方法:
1、变量的完整比较可以使用=或!=操作符
2、 部分匹配可以使用正则表达式来表示,~或~*
3、~表示区分大小写
4、~*表示不区分大小写(firefox与FireFox是一样的)
5、!~与!~* 是取反操作,也就是不匹配的意思
6、检查文件是否存在使用 -f 或 !-f 操作符
7、检查目录是否存在使用-d或!-d操作符
8、检查文件,目录或符号连接是否存在使用-e或!-e操作符
9、检查文件是否可执行使用-x或!-x操作符
10、正则表达式的部分匹配可以使用括号,匹配的部分在后面可以用$1~$9变量代替,这些和apache一致。

例子:

编辑 /usr/local/nginx/conf/nginx.conf 文件
#vi /usr/local/nginx/conf/nginx.conf
设置upstream服务器:

upstream myproxybackend  {
       server 192.168.1.1;
       server 192.168.1.2;
       server 192.168.1.3;
       server 192.168.1.4;
}
 
upstream msiebackend  {
       server 192.168.1.6;
}
 
upstream mozillabackend  {
       server 192.168.1.5;
}

更新虚拟主机配置文件

server {
      access_log  logs/access.log;
      error_log   logs/error.log;
      index       index.html;
      listen      202.54.1.5:80 default;
      root        /usr/local/nginx/html;
      server_name example.com www.example.com 0.example.com;
 
     ## PROXY - Web
      location / {
        proxy_pass  http://myproxybackend;
        if ($http_user_agent ~ MSIE ) {
              proxy_pass  http://msiebackend;
        }
        if ($http_user_agent ~ Mozilla ) {
              proxy_pass  http://mozillabackend;
        }
 
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
# many more...
# many more...
}

最后重启/重新载入nginx
#/usr/local/nginx/sbin/nginx -s reload

原创文章,转载请注明: 转自 http://salogs.com

一、目的

利用autobench工具结合httperf命令对web服务器进行测试,得出该服务器可以承载的最大并发连接数与最佳并发数。

二、测试工具

工具介绍

1、Httperf

httperf 是一款高性能的HTTP测试工具,使用它我们可以准确定位服务器的并发连接能力。下面介绍一下它的主要特征

(1) 可以观察测试客户端(并非被测服务器)在发起压力测试时的负载情况。这样在测试高并发的情况下可以准确的分析问题。(被测服务器无法承载高并发还是测试客户端无法发起过多请求)
(2)支持HTTP/1.1和SSL
(3)可以生成可扩展的测试计划

下载http://code.google.com/p/httperf/downloads/list

安装
# tar xvzf httperf-0.9.0.tar.gz
# cd httperf-0.9.0
#./configure
# make && make install

更多的使用方法参见man page。

2、autobench

autobench 是一款基于httperf的Perl脚本。它会在一次测试中调用多次httperf来对web服务器进行测试,每次会按照给定的参数增加并发连接数,将httperf的测试结果保存为CSV格式的文件,该文件可以被Excel直接读取,方便生成测试报告。借助于autobench自带的bench2graph工具可以生成漂亮的测试结果对比图,如下:

results.gif

下载:http://www.xenoclast.org/autobench/downloads/

安装

# yum install gd gnuplot pcre pcre-devel texinfo -y
# tar zxvf autobench-2.1.2.tar.gz
# cd autobench-2.1.2
# make && make install
# sed -i 's/postscript color/png xffffff/g' /usr/local/bin/bench2graph (修改bench2graph脚本,否则生成的图像背景有问题)

使用方法:参见下文在实际测试中的使用

三、测试环境

系统环境
CentOS 5.3 64bit

web软件环境
httpd-2.0.6
php5.2.6+ eAccelerator
php-fpm  开启20个php-cgi进程
nginx-0.7.67

在测服务器并发能力时会将apache与nginx对比测试

硬件环境

CPU::E5504  2.00GHz
内存:1G
虚拟机环境

四、测试方法

1、  分别测试静态文件和动态php文件
2、  静态并发数从50开始,1500结束,增长幅度为50,动态5~100,增幅为5
3、  分别测试apache和nginx的并发能力,二者进行对比
4、  每次测试进行3次,最终结果求三次平均值
5、  每进行一次测试后均重启httpd或nginx(php-fpm)服务,释放内存后再进行下一轮测试
6、  为了减少磁盘IO,均关掉了访问日志

1、开始测试

(1)静态文件

测试命令

# autobench --single_host --host1=192.168.8.8 --port1=80 --uri1=/logo.gif  --quiet  --low_rate=50 --high_rate=1500 --rate_step=50 --num_call=1 --num_conn=2000 --timeout=10 --file /tmp/result.tsv

测试结果对比分析
static.png

测试结果总结:

Apache与Nginx在并发50~1500时表现得都还可以,只不过在并发数达到1500后Apache的响应时间变得很长,由于系统环境的制约,我没有再测试大于1500的并发连接情况,但可以对比看出nginx在1500个并发连接的情况下还能保持较低的响应时间。

(2)动态文件

测试命令

# autobench --single_host --host1=192.168.8.8 --port1=80 --uri1=/test.php --quiet --low_rate=5 --high_rate=100 --rate_step=5 --num_call=1 --num_conn=200 --timeout=10 --file /tmp/nginx_php1.tsv

测试结果数据

并发连接数 nginx实际并发数 apache实际并发数 nginx应答时间 apache应答时间
5.0 5.0 5.0 36.0 36.3
10.0 10.0 10.0 33.0 31.7
15.0 15.0 15.0 35.6 31.9
20.0 19.9 19.9 36.8 32.3
25.0 25.0 25.0 42.6 36.2
30.0 29.8 29.7 45.1 67.6
35.0 34.3 32.4 115.5 248.5
40.0 35.5 34.9 396.8 427.3
45.0 35.3 33.4 699.0 865.8
50.0 35.7 30.5 962.3 1394.0
55.0 35.5 33.3 1103.9 1354.7
60.0 35.7 32.9 1269.2 1471.3
65.0 34.9 33.5 1445.8 1573.6
70.0 37.6 29.7 1458.0 2049.5
75.0 37.2 35.9 1610.1 1496.8
80.0 23.9 31.2 1588.2 1993.3
85.0 24.7 33.2 1674.9 1880.2
90.0 37.1 34.5 1838.6 1946.0
95.0 35.0 30.3 2027.4 2387.2
100.0 35.3 36.4 1996.3 1904.5

测试数据

测试结果对比图
total_php-2.png

测试总结

由上面的报表以及这张曲线图可以看出,无论是apache还是nginx其php并发大于30其响应时间的就会直线上升,nginx略好于apache,因此可以判断这台服务器的php并发极限在30左右,当然这里指的php连接中是没有连接数据库的,也没有加入memcached等缓存机制。

(3)高并发下系统资源情况

CPU使用情况对比
cpu.png


内存使用情况对比

memory.png

系统负载对比
load.png

分析测试结果

CPU使用情况
在极限并发的情况下apache和nginx都占用很多CPU资源,这也是情理之中的事          情。nginx略好于apache平均占用90%而apache则在95%左右

内存使用情况
在内存对比中可以清楚的看到,nginx在极限并发的情况下内存控制得很好,到达一定程度后就不在变化了,而apache则会直线上升

负载情况
负载情况与内存情况类似,如果高并发时间很长的话apache服务器绝对会挂掉!nginx的负载也很高,但一直保持在10以下,这也和内存占用有关。

五、结论

利用httperf结合autobench可以很方便的测试出单台服务器的极限并发数,这样对服务器性能评估有很大帮助,借助于autobench的bench2graph脚本可以生成更为直观的对比图。

针对被测服务器,经过apache与nginx的对比发现,在静态文件的处理方面如果并发小于1500,apache和nginx之间的差距还是很小的。在动态php文件的并发测试中,nginx体现出其强大的性能优势,如果内存足够大,通过调整php-cgi数量,相信可以承载更多的并发连接。

六、附录

1、问题解决

(1)当运行时报如下错误

httperf: warning: open file limit > FD_SETSIZE; limiting max. # of open files to FD_SETSIZE

解决方法

其意思是说在httperf在发起连接请求时,单个进程已经无法再打开更多的文件描述符。在发起连接请求时httperf使用select()方法使用一个新的文件描述符。因此需要增加文件描述符限制

步骤1:编辑/etc/security/limits.conf 在最后添加下面两行内容
*       hard    nofile          102400
*       soft    nofile          102400

步骤2:编辑 /usr/include/bits/typesizes.h 文件修改__FD_SET_SIZE常量值,如下
#define __FD_SETSIZE            1024
修改为
#define __FD_SETSIZE            102400

步骤3:重新编译httperf

2、参考文章

http://www.cppblog.com/qiujian5628/archive/2008/03/10/44060.html
http://wiki.nginx.org/
http://www.joeandmotorboat.com/2008/02/28/apache-vs-nginx-web-server-performance-deathmatch/

原创文章,转载请注明: 转自 http://salogs.com

漏洞介绍:nginx是一款高性能的web服务器,使用非常广泛,其不仅经常被用作反向代理,也可以非常好的支持PHP的运行。80sec发现其中存在一个较为严重的安全问题,默认情况下可能导致服务器错误的将任何类型的文件以PHP的方式进行解析,这将导致严重的安全问题,使得恶意的攻击者可能攻陷支持php的nginx服务器。

漏洞分析:nginx默认以cgi的方式支持php的运行,譬如在配置文件当中可以以

location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi_params;
}

的方式支持对php的解析,location对请求进行选择的时候会使用URI环境变量进行选择,其中传递到后端Fastcgi的关键变量 SCRIPT_FILENAME由nginx生成的$fastcgi_script_name决定,而通过分析可以看到$fastcgi_script_name是直接由URI环境变量控制的,这里就是产生问题的点。而为了较好的支持PATH_INFO的提取,在PHP 的配置选项里存在cgi.fix_pathinfo选项,其目的是为了从SCRIPT_FILENAME里取出真正的脚本名。
那么假设存在一个http://www.80sec.com/80sec.jpg,我们以如下的方式去访问

http://www.80sec.com/80sec.jpg/80sec.php

将会得到一个URI
/80sec.jpg/80sec.php

经过location指令,该请求将会交给后端的fastcgi处理,nginx为其设置环境变量SCRIPT_FILENAME,内容为

/scripts/80sec.jpg/80sec.php

而在其他的webserver如lighttpd当中,我们发现其中的SCRIPT_FILENAME被正确的设置为

/scripts/80sec.jpg

所以不存在此问题。
后端的fastcgi在接受到该选项时,会根据fix_pathinfo配置决定是否对SCRIPT_FILENAME进行额外的处理,一般情况下如果不对fix_pathinfo进行设置将影响使用PATH_INFO进行路由选择的应用,所以该选项一般配置开启。Php通过该选项之后将查找其中真正的脚本文件名字,查找的方式也是查看文件是否存在,这个时候将分离出SCRIPT_FILENAME和PATH_INFO分别为

/scripts/80sec.jpg和80sec.php

最后,以/scripts/80sec.jpg作为此次请求需要执行的脚本,攻击者就可以实现让nginx以php来解析任何类型的文件了。

POC: 访问一个nginx来支持php的站点,在一个任何资源的文件如robots.txt后面加上/80sec.php,这个时候你可以看到如下的区别:

访问http://www.80sec.com/robots.txt

HTTP/1.1 200 OK
Server: nginx/0.6.32
Date: Thu, 20 May 2010 10:05:30 GMT
Content-Type: text/plain
Content-Length: 18
Last-Modified: Thu, 20 May 2010 06:26:34 GMT
Connection: keep-alive
Keep-Alive: timeout=20
Accept-Ranges: bytes

访问访问http://www.80sec.com/robots.txt/80sec.php

HTTP/1.1 200 OK
Server: nginx/0.6.32
Date: Thu, 20 May 2010 10:06:49 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
X-Powered-By: PHP/5.2.6

其中的Content-Type的变化说明了后端负责解析的变化,该站点就可能存在漏洞。

漏洞厂商:http://www.nginx.org

解决方案:

我们已经尝试联系官方,但是此前你可以通过以下的方式来减少损失

关闭cgi.fix_pathinfo为0

或者

if ( $fastcgi_script_name ~ \..*\/.*php ) {
return 403;
}

PS: 鸣谢laruence大牛在分析过程中给的帮助

转自:http://www.80sec.com/nginx-securit.html


看过这篇文章,和同事讨论了一下,其中上文提到的解决方法2,实际上还是无法解决问题的。如果允许用户上传,最好还是在允许上传文件的程序中判断用户上传的文件类型,如:是否为text/xxx类型的文件,如果是则还需要再判断该文件中是否有可执行的php脚本。

针对解决方法1,不建议修改,其可能会影响正常程序的运行。

以下是我们的处理方法:限制uploads目录下的php程序执行

...
    location ~ ^/.+\.php(/.*)?$ {
      set $script    $fastcgi_script_name;

      if ($script ~* "^/uploads/.+"){
        return 404;
      }
...

测试目的

(1)弄清楚HTTP Upstream 模块中Server指令的max_failsfail_timeout参数的关系、它们对后端服务器健康情况的检查起到了什么作用、它们的取值对Http proxy模块中的其它指令是否有直接或间接的影响等……

(2)测试HTTP Proxy模块中proxy_next_upstream、proxy_connect_timeout、proxy_read_timeout、proxy_send_timeout指令的作用、对nginx性能的影响、对后端服务器响应的处理等……

测试方法

本文测试不会使用压力测试,所有的测试都是通过浏览器手动刷新来实现的。后端服务器使用简单的php程序来实现。

测试环境

Nginx负载均衡/反向代理服务器
系统:CentOS 5.4 64bit
Nginx:0.7.65
IP:192.168.108.10

后端web服务器
系统:CentOS 5.4 64bit
Web环境:apache+php
Web-1 IP:192.168.108.163
Web-2 IP:192.168.108.164

本次测试主要针对HTTP Upstream和HTTP Proxy模块进行,下面测试环境中http upstream 和http proxy模块参数的初始化设置,后文会针对测试的参数进行相应的修改:


upstream test  {
server 192.168.108.163 ;
server 192.168.108.164:80;
}

server {
listen          80;
server_name     .test.com;
index           index.php index.html index.htm;

location / {
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404;

proxy_connect_timeout       10s;
proxy_read_timeout          2s;
#proxy_send_timeout          10s;
proxy_pass http://test;
}
}

提出server指令后面的参数部分,以下摘抄nginx wiki 内容

语法:server name [parameters]

parameters包含:

·weight = NUMBER - 设置服务器权重,默认为1。

·max_fails = NUMBER - 在一定时间内(这个时间在fail_timeout参数中设置)检查这个服务器是否可用时产生的最多失败请求数,默认为1,将其设置为0可以关闭检查,这些错误在proxy_next_upstream或fastcgi_next_upstream(404错误不会使max_fails增加)中定义。

·fail_timeout = TIME - 在这个时间内产生了max_fails所设置大小的失败尝试连接请求后这个服务器可能不可用,同样它指定了服务器不可用的时间(在下一次尝试连接请求发起之前),默认为10秒,fail_timeout与前端响应时间没有直接关系,不过可以使用proxy_connect_timeout和 proxy_read_timeout来控制。

·down - 标记服务器处于离线状态,通常和ip_hash一起使用。

·backup - (0.6.7或更高)只用于本服务器,如果所有的非备份服务器都宕机或繁忙。

关于max_fails参数的理解根据上面的解释,max_fails默认为1,fail_timeout默认为10秒,也就是说,默认情况下后端服务器在10秒钟之内可以容许有一次的失败,如果超过1次则视为该服务器有问题,将该服务器标记为不可用。等待10秒后再将请求发给该服务器,以此类推进行后端服务器的健康检查。但如果我将max_fails设置为0,则代表不对后端服务器进行健康检查,这样一来fail_timeout参数也就没什么意义了。那若后端服务器真的出现问题怎么办呢?上文也说了,可以借助proxy_connect_timeout和proxy_read_timeout进行控制。

下面介绍http proxy模块中的相关指令:

proxy_next_upstream
语法: proxy_next_upstream [error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_404|off]
确定在何种情况下请求将转发到下一个服务器。转发请求只发生在没有数据传递到客户端的过程中。

proxy_connect_timeout
后端服务器连接的超时时间_发起握手等候响应超时时间

proxy_read_timeout
连接成功后_等候后端服务器响应时间_其实已经进入后端的排队之中等候处理(也可以说是后端服务器处理请求的时间)

proxy_send_timeout
后端服务器数据回传时间_就是在规定时间之内后端服务器必须传完所有的数据

proxy_pass
这个指令设置被代理服务器的地址和被映射的URI

开始测试

情况1:后端程序执行时间超过或等于proxy_read_timeout设置值,max_fails=0 关闭后端服务器健康检查。

Nginx配置修改内容 server 192.168.108.163 max_fails = 0;
server 192.168.108.164 max_fails = 0;
proxy_next_upstream error timeout
proxy_read_timeout 2s
后端web服务器
Web1 test.php Web2 test.php
<?php
header('RS:Web1');
$t = 2;
sleep($t);
echo "sleep {$t}s<br>";
echo "web-1<br>";
?>
<?php
header('RS:Web2');
$t = 5;
sleep($t);
echo "sleep {$t}s<br>";
echo "web-2<br>";
?>
备注:

我这里的两台后端web服务器,他们的主页文件均为一个test.php程序,该程序分别sleep了2秒和5秒,等于和超过了proxy_read_timeout的时间,[max_fails=0] 即关闭后端服务器健康检查。[proxy_next_upstream error timeout] 说明碰到错误或超时的情况切到下一个后端服务器。如此设置后利用curl命令对nginx发起连接请求,看nginx会作何反应。

测试开始:

(1)curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer} www.test.com/test.php
HTTP/1.1 504 Gateway Time-out
Server: nginx/0.7.65
Date: Tue, 18 May 2010 02:43:08 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 183
Connection: keep-alive

4.008:0.002:4.007

说明:

连续请求3次后得到的http返回结果是一样的,均为504 Gateway Time-out 错误。这种情况只有在后端服务器都有问题的时才会出现这个错误,很显然我这里的proxy_read_timeout设置的时间太短,后端程序还没来得及把程序执行完,nginx就迫不及待的将请求甩给upstream定义的另一台服务器上了,当发现另外一台服务器同样2秒没有返回后,nginx这回没有服务器可用,只有返回504 Gateway Time-out 。这也是为什么最后的time_total时间是4秒。(经查看两台web服务器的访问日志得知,均有一条访问记录,且返回代码为200,说明nginx确实来过,但没有等到执行完成就匆匆的离去了)如果我有3台服务器,在保证任何不变的情况下,time_total时间一定会是6秒,因为nginx会一个接一个的将3台服务器都走一遍。

-----------------------------------------------------------------------------------------------------------------------

好了,确认是我proxy_read_timeout设置时间太短后,我将它的值设置为3秒,再通过curl命令访问:

(2)curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer} www.test.com/test.php
HTTP/1.1 200 OK
Server: nginx/0.7.65
Date: Tue, 18 May 2010 03:07:58 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/5.1.6
RS: Web1

5.042:0.005:5.042

说明:通过3次连续请求,得到的结果是一样的,RS:Web1 也就是说我这三次的请求都甩到了web1上。但我web1中的程序只需要2秒后就可以返回结果,但为什么我通过nginx代理后时间总是我的 程序执行时间+proxy_read_timeout时间呢?

-----------------------------------------------------------------------------------------------------------------------

继续将proxy_read_timeout设置为4s

(3)curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer} www.test.com/test.php
HTTP/1.1 200 OK
Server: nginx/0.7.65
Date: Tue, 18 May 2010 03:15:25 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/5.1.6
RS: Web1

6.004:0.000:6.004

三次请求后结果也是一样,这次花的时间更长了,但确实是程序执行时间+proxy_read_timeout 时间。但为什么每次都需要6秒呢?按照upstream中定义的权重应该是平分请求的,最起码应该有2秒的时候。经过分析得知:最终返回给用户请求的是web1,那么当再次请求的时候一定会分给web2,由于web2是sleep 5秒的,因此经过proxy_read_timeout的时间(4s)后会跳到web1,结果还是web1返回的请求,所花时间就是nginx在web2等待的时间+web1执行的时间,以此类推下一次nginx自然的还会分给web2……。如果有更多的后端web,则判断下一个请求服务器可以看当前返回给最终用户的是那台服务器,然后根据upstream中定义的顺序向下查询(权重一样的情况)

结论:

(1)上面的三次测试分别将proxy_read_timeout的值设置为2s、3s、4s的情况进行的。最终的测试结果也都在后面做了解释与说明。由于我关闭了后端服务器的健康检查(max_fails=0)因此判断后端服务器情况的唯一依据便是proxy_read_timeout参数,如果这个参数设置得过小,但后端程序的执行或多或少会超过这个时间的话,这种情况nginx的效率是非常低的。

(2)上面的测试都是后端服务器正常但执行超时的情况下nginx根据proxy_read_timeout和proxy_next_upstream的值来选择下一个服务器,那如果我后端服务器直接报错的情况呢?可以想到如果报错信息在proxy_next_upstream 中有定义的话nginx还会跳到下一台服务器。否则直接将保存信息返回给nginx从而最终呈献给用户

情况2:打开后端服务器健康检查,测试程序执行时间超过或等于proxy_read_timeout值或后端服务器直接报错的情况

Nginx配置修改内容 server 192.168.108.163 max_fails = 1;
server 192.168.108.164 max_fails = 1;
proxy_next_upstream error timeout http_500 http_502 http_504
proxy_read_timeout 2s
后端web服务器
Web1 test.php Web2 test.php
<?php
header('RS:Web1');
$t = 2;
sleep($t);
echo "sleep {$t}s<br>";
echo "web-1<br>";
?>
<?php
header('RS:Web2');
header('http/1.1 500 Internal Server Error ');
#$t = 5;
#sleep($t);
echo "sleep {$t}s<br>";
echo "web-2<br>";
?>
备注:

开启了后端服务器健康检查
proxy_read_timeout 2s (下面会随着测试变更)
Web1程序仍然sleep 2s
修改了Web2程序,让他直接返回500错误

测试开始:

(1)连续测试三次结果如下:

curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer} www.test.com/test.php
HTTP/1.1 504 Gateway Time-out
Server: nginx/0.7.65
Date: Tue, 18 May 2010 07:01:48 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 183
Connection: keep-alive

2.005:0.001:2.005

curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer} www.test.com/test.php
HTTP/1.1 502 Bad Gateway
Server: nginx/0.7.65
Date: Tue, 18 May 2010 07:01:50 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 173
Connection: keep-alive

0.001:0.001:0.001

curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer} www.test.com/test.php
HTTP/1.1 504 Gateway Time-out
Server: nginx/0.7.65
Date: Tue, 18 May 2010 07:01:57 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 183
Connection: keep-alive

2.005:0.001:2.005

说明:

第1次请求所用时间是2秒,web1执行超时,web2返回了500错误,upstream没有更多的后端,因此nginx直接把504扔出来了,同时标记web2,web1不可用。查看后端2台web服务器的访问日志,均有nginx代理的访问记录。
第2次请求时间很短,报502错误,说明没有可用的后端服务器接受请求。查看后端两台web服务器访问日志,没有任何变化,说明这两台服务器被nginx标记为不可用,没有把请求转向后端,直接返回用户502错误
第3次请求同第1次

(2)修改 proxy_read_timeout 3s 连续访问6次后结果以及2台web服务器的日志情况
curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer} www.test.com/test.php
HTTP/1.1 200 OK
Server: nginx/0.7.65
Date: Tue, 18 May 2010 07:30:15 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/5.1.6
RS: Web1

2.003:0.001:2.002

访问日志

Web1
[18/May/2010:15:30:00
[18/May/2010:15:30:03
[18/May/2010:15:30:05
[18/May/2010:15:30:08
[18/May/2010:15:30:11
[18/May/2010:15:30:13

Web2

[18/May/2010:15:30:00
[18/May/2010:15:30:11

说明:

由访问日志可知:
第1次请求是被分到web2上的,由于它返回了500错误,因此请求被转到web1,并标记web2不可用。
第2次至第4次均将请求给了web1,第四次请求完毕后距第一请求已经过去了8秒。
第5次请求时已经是fail_timeout参数默认的10s也就是标记web2不可用的时间已经过去了,因此在第5次访问实际上和第一次情况是一样的。

结论:

(1proxy_next_upstream参数很有用,他可以避免很多错误
(2max_fails 参数在繁忙的大型系统中建议设置为3,如果没有几个后端服务器的话保持默认即可。
(3proxy_read_timeout要根据自身程序而定,不要过大,也不要太小。如果是php程序,请参照php.ini中的max_execution_time选项值。

原创文章,转载请注明:   转自 http://salogs.com

需求比标题复杂些,如下:

(1)在dns注册 *.test.com的域名指向一台服务器,如:210.87.12.5
(2)在这台服务器上,安装有web程序,该程序可以根据用户输入的二级域名跳转到该用户的产品主页
(3)对于非法的域名指向,进行rewrite处理(返回404错误,否则一经被查,断电处理)
(4)用户访问合法的域名但不是https连接的,跳转到https页面(即若是80端口则跳转到443端口)
(5)对于指定的二级域名,只准许某个(些)ip访问

针对各个需求作如下处理:

需求1:简单,在dns注册泛域名即可

需求2:纯粹的程序处理,略过

需求3:rewrite规则如下:

RewriteCond %{HTTP_HOST} !^abc|^apple|^banana|^demo\.test\.com [NC]
RewriteRule ^/$ default [R=404]

即:不是我上面列出的域名,其他的都返回404错误。这里注意每一项的前面都要加^

需求4:rewrite规则如下:

RewriteCond %{SERVER_PORT} !^443$
RewriteRule ^/?(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]

需求5:rewrite规则如下:

RewriteCond %{REMOTE_ADDR} !^124.11.12.141 [OR]
RewriteCond %{REMOTE_ADDR} !^124.11.12.142 [NC]
RewriteCond %{HTTP_HOST} ^abc\.test\.com [NC]
RewriteRule ^/$ default [R=404]

即:如果用户的ip不是124.11.12.141或124.11.12.142但却访问了 abc.jobkoo.com 则直接返回404错误,也就是说只有列出的ip可以访问。

注意:

(1)由于本机同时开放了80和443端口,因此在设置rewrite规则时需要在两端口的虚拟主机配置中同时添加。
(2)rewrite是有顺序的,我这里的顺序如下:

# 限制ip访问域名
RewriteCond %{REMOTE_ADDR} !^124.11.12.141 [OR]
RewriteCond %{REMOTE_ADDR} !^124.11.12.142 [NC]
RewriteCond %{HTTP_HOST} ^abc\.test\.com [NC]
RewriteRule ^/$  default [R=404]

# 拒绝非法域名指向
RewriteCond %{HTTP_HOST} !^abc|^apple|^banana|^demo\.test\.com [NC]
RewriteRule ^/$ default [R=404]

# 非443端口跳转
RewriteCond %{SERVER_PORT} !^443$
RewriteRule ^/?(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]

原创文章,转载请注明: 转自 http://salogs.com

最后更新于:2010年06月18日10:58

在配置多个SSL的虚拟主机的时候,很容易想当然的像配置普通HTTP虚拟主机一样,新建一个Virtualhost 后reload服务器。可用浏览器访问的时候,却提示证书为已经存在的某个虚拟主机的SSL证书,造成配置失败。
网上查询得知,一个普通的SSL证书是独占服务器端口。也就是说,如果Apache服务器上的虚拟主机A使用了SSL_A,并监听端口443,那即使新配置了虚拟主机B,如果虚拟主机是按照配置的,那用户访问时候读取的仍然是SSL_A。这和普通虚拟空间可以通过设置域名来区别虚拟主机是不同的。

要解决这个问题,实现一个Apache服务器上提供多个SSL虚拟主机,可以:

1、使用多域名SSL证书,可以实现一个IP,一个443端口上多个SSL虚拟主机;
为所有SSL虚拟主机配置单独的端口。比如,默认的虚拟主机使用443,其他的使用8080或8081等,且每个SSL虚拟主机必须独占一个端口;
2、为Apache服务器配置多个IP,每个SSL虚拟主机独占IP。如果只有一张物理网卡,可以配置为网卡配置子接口;
3、使用mod_gnutls模块,创建多个SSL虚拟主机

总之,Apache的SSL虚拟主机配置需要保证每个SSL虚拟主机有独立的IP:Port组合,如果不能提供这个条件,那只好多域名SSL证书了。

如今利用nginx做负载均衡的实例已经很多了,针对不同的应用场合,还有很多需要注意的地方,本文要说的就是在通过CDN 后到达nginx做负载均衡时请求头中的X-Forwarded-For项到底发生了什么变化。下图为简单的web架构图:

nginx_x_forwarded_for.png
nginx 负载均衡

先来看一下X-Forwarded-For的定义:
X-Forwarded-For:简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP,只有在通过了HTTP 代理或者负载均衡服务器时才会添加该项。它不是RFC中定义的标准请求头信息,在squid缓存代理服务器开发文档中可以找到该项的详细介绍。
标准格式如下:
X-Forwarded-For: client1, proxy1, proxy2
从标准格式可以看出,
X-Forwarded-For头信息可以有多个,中间用逗号分隔,第一项为真实的客户端ip,剩下的就是曾经经过的代理或负载均衡的ip地址,经过几个就会出现几个。

按照上图的Web架构图,可以很容易的看出,当用户请求经过CDN后到达Nginx负载均衡服务器时,其X-Forwarded-For头信息应该为 客户端IP,CDN的IP 但实际情况并非如此,一般情况下CDN服务商为了自身安全考虑会将这个信息做些改动,只保留客户端IP。我们可以通过php程序获得X-Forwarded-For信息或者通过Nginx的add header方法来设置返回头来查看。

下面来分析请求头到达Nginx负载均衡服务器的情况;在默认情况下,Nginx并不会对X-Forwarded-For头做任何的处理,除非用户使用proxy_set_header 参数设置:
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

$proxy_add_x_forwarded_for变量包含客户端请求头中的"X-Forwarded-For",与$remote_addr用逗号分开,如果没有"X-Forwarded-For" 请求头,则$proxy_add_x_forwarded_for等于$remote_addr。

$remote_addr变量的值是客户端的IP

当Nginx设置X-Forwarded-For等于$proxy_add_x_forwarded_for后会有两种情况发生

1、如果从CDN过来的请求没有设置X-Forwarded-For头(通常这种事情不会发生),而到了我们这里Nginx设置将其设置为$proxy_add_x_forwarded_for的话,X-Forwarded-For的信息应该为CDN的IP,因为相对于Nginx负载均衡来说客户端即为CDN,这样的话,后端的web程序时死活也获得不了真实用户的IP的。

2、CDN设置了X-Forwarded-For,我们这里又设置了一次,且值为$proxy_add_x_forwarded_for的话,那么X-Forwarded-For的内容变成 ”客户端IP,Nginx负载均衡服务器IP“如果是这种情况的话,那后端的程序通过X-Forwarded-For获得客户端IP,则取逗号分隔的第一项即可

如上两点所说,如果我们知道了CDN设置了X-Forwarded-For信息,且只有客户端真实的IP的话,那么我们的Nginx负载均衡服务器可以不必理会该头,让它默认即可。

其实Nginx中还有一个$http_x_forwarded_for变量,这个变量中保存的内容就是请求中的X-Forwarded-For信息。如果后端获得X-Forwarded-For信息的程序兼容性不好的话(没有考虑到X-Forwarded-For含有多个IP的情况),最好就不要将X-Forwarded-For设置为 $proxy_add_x_forwarded_for。应该设置为$http_x_forwarded_for或者干脆不设置!

参考文章:http://en.wikipedia.org/wiki/X-Forwarded-For

原创文章,转载请注明: 转自 http://salogs.com

Google近日推出了一款网站性能优化工具:Page Speed(http://code.google.com/speed/page-speed/)。它旨在帮助站长与网站开发者分析网站中存在的性能方面的问题,并有针对性地提出改进意见。Page Speed在功能方面极其类似于Yahoo!的网站性能优化YSlow,不过YSlow要比Page Speed推出早得的多。它们都是基于Firebug的Fireffox插件,使用方法也类似。

一、Page Speed的安装及使用

Page Speed是一款Firefox插件,同时他依附于别款插件Firebug,也就是说你的Firefox浏览器中必须已经安装了 Firebug才能安装Page Speed。安装环境为Firefox 3.0.4以上,Fireug 1.3.3以上。

Page Speed的使用也很简单,在Firefox中点击右下角的Firebug图标启动后,再点击Page Speed选项卡即可。要注意的是,你要对你网站内的某个页面进行性能分析,你必须先把该页面加载完成后才能使用Page Speed,也就是说只有在浏览器左下角出现“Done”或者"完成"之后才可以启用Page Speed进行分析。如果页面中流媒体,可能不会现在“完成”,这种情况要等到流媒体可以播放。

clip_image002

然后点击“Analyze Performance”(性能分析),这时Page Speed会根据web performance best practices (网页性能最佳实践)进行逐项打分。然后根据重要程序和优先级对每项进行排列。

clip_image004

此外,你还可以点击每条建议前面的“加号”展开查看详细的描述,或者直接点击每条规则相看该规则的具体内容,还可以点击“Show Resource”(查看来源)来查看每条建议是针对页面中哪部分内容提出的。

对于分析结果中的符号说明一下:

  1. 红色感叹号代表高优先级提示,表示这一项严重影响了你的页面性能,你需要优先对其进行性能优化;
  2. 橙色三角代表此项提示需要引起你的注意,并进行适当改进;
  3. 绿色的对号代表该项规则在你的网站中应用得到,你在修改了前面两部分的提示之后,它们有可能变为绿色的对号;
  4. 蓝色消息符号是为你提供了额外的帮助信息,请稍加留意(需要注意的是,如果你的页面中出现了大量的此类符号,可能是因为你在页面加载完成之前就进 行了网站性能分析)。

二、活动记录

活动记录是一条页面活动的时间轴,它记录了包括网络事件、JavaScript运行在内的所有浏览器活动。你可以使用它并配合性能分析中的数据进一 步对网站性能做出评估。

  • 查看页面运行过程中所耗费的时间,以毫秒计算;
  • 查看浏览器事件,包括页面加载完成后的事件;
  • 区分造成页面响应缓慢的原因,其中包括网络来时、DNS查找、连接建立、JavaScript运行等;
  • 获取在特定时间或者事件下才响应的JavaScript事件列表;
  • 可以对其它标签或者窗口中打开的页面进行分析;
  • 多页面加载时的页面加载顺序;
  • 对根据Page Speed优化前后的表现进行对比。

clip_image006

三、理解Page Speed中的事件

页面记录选项卡下是通过时间线来记录各种资源加载到页面所有需要的时间。事件的记录时间间隔为10毫秒,如果事件需要的时间少于10毫秒那么它将用 较短的色块来表示。时间线中没有任何颜色的表示,在浏览器事件的运行依赖于其它进程,如DOM和CSS渲染、Flash ActionScript、渲染、操作系统事件等。

网络
事件

描述

image

DNS

浏览器查找DNS所需要的时间

image

t连接等待

浏览器与网站服务器建立连接(TCP)需要一定的时间。由于浏览器可以打开的连接数目是有限的,如果达到这个限制他必须 等其它连接关闭之后才能再重新建立一个新的连接。(更多关于浏览器连接的信息可以参照Parallel downloads across hostnames)。 这个事件显示了浏览器等其它连接完成的时间。

image

连接

浏览器和web服务器建立连接。这个事件只有打开新连接时出现,已有连接重新打开使用不包含在内。

image

请求发送

浏览器发送的HTTP请求。只显示GET方式的请求。

image

已连接

浏览器通过网络等待接收数据。事件随着浏览器TCP连接的结束而结束。

本地
事件

描述

image

缓存

浏览器成功将内容加入到缓存中。

image

可用数据

可用于浏览器呈现的数据。由于web服务器发送大量的数据,如果文件很大那么有可能一个资源会出现多个该事件。

image

获取JS

浏览器获取JavaScript。该事件可能会延缓其它事件,如果此种情况出现,将会在其下一行列出。

image

运行JS

浏览器执行JavaScript。该事件可能会延缓其它事件,如果此种情况出现,将会在其下一行列出。如果获取JS和运 行JS中间有时间间隔,这说明源文件中包括有延时功能的函数。

此外,Page Speed还包括了对已完成的JavaScript函数的信息搜集功能,当页面中的JS函数一旦运行,PageSpeed就会捕捉到相关信息。不通过对 Page Speed进行设置还可以对未触发函数、延时加载函数等进行收集。

下面的图片显示了7800毫秒时已经加载但还未触发的函数列表:

clip_image008

而下面则显示是已经触发运行了的JS函数:

clip_image010

此外Pge Speed还有诸如JavaScript函数控制、浏览器User Agent设置等更高级功能。具体使用大家可以与YSlow对比一下。

相信,用好这两款工具,对于站长和网站开发者来说会有极大的帮助。


需求1:CDN小节点使用尽可能少的资源实现高可用和负载均衡
需求2:需要支持10多个HTTPS站点的反向代理
后端环境:nginx在前端做url_hash,后端缓存服务器使用squid和lighttpd分别处理动静态内容,服务器证书在nginx上
解决方案:
使用keepalived来实现前端3台nginx的高可用,DNS轮询来均摊负载。
每个https站点使用一个独立IP,非https站点分摊到这些IP上。
在每台机器的lo上绑定所有的IP来启动nginx服务。

架构图:

原理:
正常情况下负载均摊,每台机器只负担2-3个HTTPS站点,比如此时nginxA只负责192.168.0.101和192.168.0.102这2个HTTPS站点。当nginxB挂掉时,nginxA和nginxC会根据策略去分担192.168.0.103和192.168.0.104这2个站点的访问,这时nginxA上负担的站点就可能就是192.168.0.101、192.168.0.102和192.168.0.104了。

分配策略:

lo绑ip脚本:

#!/bin/sh
echo '2' > /proc/sys/net/ipv4/conf/lo/arp_announce
echo '1' > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo '2' > /proc/sys/net/ipv4/conf/all/arp_announce
echo '1' > /proc/sys/net/ipv4/conf/all/arp_ignore

ifconfig lo:1 198.188.225.101 netmask 255.255.255.255
ifconfig lo:2 198.188.225.102 netmask 255.255.255.255
ifconfig lo:3 198.188.225.103 netmask 255.255.255.255
ifconfig lo:4 198.188.225.104 netmask 255.255.255.255
ifconfig lo:5 198.188.225.105 netmask 255.255.255.255
ifconfig lo:6 198.188.225.106 netmask 255.255.255.255

Ngnix A配置文件范例:
! Configuration File for keepalived

global_defs {
notification_email {
du@nli0n@snda.com
}
notification_email_from nginx_ha1@snda.com
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id nginx_ha1
}

vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.0.101
192.168.0.102
}
}

vrrp_instance VI_2 {
state BACKUP
interface eth0
virtual_router_id 52
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.0.103
192.168.0.104
}
}

vrrp_instance VI_3 {
state BACKUP
interface eth0
virtual_router_id 53
priority 50
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.0.105
192.168.0.106
}
}