03/28/14

nginx 泛解析实例

一、需求:

1、*.phpdba.com 解析到这台服务器

2、这台服务器不做301或者302跳转,直接将请求交由user.phpdba.com处理

二、nginx配置

server {
listen      80 ;
server_name  user.phpdba.com *.phpdba.com;

set $host_main ‘user.phpdba.com’;

location /        {
proxy_pass  http://192.168.0.253:80;
proxy_set_header   Host   $host_main;
proxy_set_header   X-Real-IP        $remote_addr;
proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
proxy_hide_header X-Powered-By;
}

location ~ /\.(ht|svn) {
deny  all;
}

}

11/15/13

静态资源站用nginx替换apache提供服务

需求:静态资源站点迁移,nginx实现当前服务器不存在访问的资源时,回源服务器提取并保存到当前服务器,并去掉apache环节,直接用nginx提供服务。

源站 apache:

RewriteRule ^/x_avatar/v[0-9]+/(.*)$ /x_avatar/$1 [L]

当前服务器 nginx:

location ^~ /x_avatar/v([0-9]+)/(.*)$ {
set $x_avatar_file $2;
rewrite ^/x_avatar/v([0-9]+)/(.*)$ /x_avatar/$2 break;
#root /opt/www/html/phpdba/;

proxy_temp_path /opt/www/html/phpdba/;
expires 3h;
proxy_store on;
proxy_store_access user:rw group:rw all:rw;
proxy_set_header Accept-Encoding ”;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_hide_header X-Powered-By;
proxy_cache_valid 200 302 1h;
proxy_cache_valid 301 1d;
if ( !-e $document_root/x_avatar/$x_avatar_file) {
proxy_pass  http://xxx.xxx.xxx.xxx:xx;
}
}

location /   {
root /opt/www/html/phpdba/;
proxy_temp_path /opt/www/html/phpdba/;
expires 3h;
proxy_store on;
proxy_store_access user:rw group:rw all:rw;
proxy_set_header Accept-Encoding ”;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_hide_header X-Powered-By;
proxy_cache_valid 200 302 1h;
proxy_cache_valid 301 1d;
if ( !-e $request_filename) {
proxy_pass  http://xxx.xxx.xxx.xxx:xx ;
}
}

10/23/13

nginx做前端代理,程序取不到HTTP_X_FORWARDED_FOR值

用nginx做前端代理后,我们发现 HTTP_X_FORWARDED_FOR 无法获取到客户端真实的IP地址。

原因 : nginx 默认并不会增加 X_FORWARDED_FOR 头信息,我们给他加上就好了。

简单配置如下:

  1. location /
  2. {
  3.     proxy_pass          http://www.phpdba.com;
  4.     proxy_set_header    Host             $host;
  5.     proxy_set_header    X-Real-IP        $remote_addr;
  6.     proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;    //别忘了这一句
  7.     proxy_set_header    HTTP_X_FORWARDED_FOR $remote_addr;              //或是加上这一句
  8.     proxy_redirect      default;
  9. }

重启nginx加载新配置后,就可以获取客户端真实的IP地址了。

10/23/13

静态文件响应POST请求,提示405错误问题

例1:用linux下的curl命令发送POST请求给Apache服务器上的HTML静态页

  1. [root@localhost ~]# curl -d 11=1 http://blog.phpdba.com/index.html
  2. <!DOCTYPE HTML PUBLIC “-//IETF//DTD HTML 2.0//EN”>
  3. <HTML>
  4.     <HEAD>
  5.         <TITLE>405 Method Not Allowed</TITLE>
  6.     </HEAD>
  7.     <BODY>
  8.         <H1>Method Not Allowed</H1>
  9.         The requested method POST is not allowed for the URL /index.html.<P>
  10.         <HR>
  11.         <ADDRESS>Apache/1.3.37 Server at blog.phpdba.com Port 80</ADDRESS>
  12.     </BODY>
  13. </HTML>

例2:用linux下的curl命令发送POST请求给nginx服务器上的HTML静态页

  1. [root@localhost ~]# curl -d 11=1 http://www.phpdba.com/index.htm
  2. <html>
  3.     <head><title>405 Not Allowed</title></head>
  4.     <body bgcolor=”white”>
  5.         <center><h1>405 Not Allowed</h1></center>
  6.         <hr><center>nginx/1.2.0</center>
  7.     </body>
  8. </html>

但在有些应用中,需要使静态文件能够响应POST请求。
对于Nginx,可以修改nginc.conf配置文件,改变“405错误”为“200 ok”,并配置location来解决,方法如下:

  1. server
  2. {
  3.     listen  80;
  4.     server_name www.92csz.com;
  5.     index index.html index.htm index.php;
  6.     root  /opt/htdocs;
  7.     if (-d $request_filename)
  8.     {
  9.         rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent;
  10.     }
  11.     error_page  405 =200 @405;
  12.     location @405
  13.     {
  14.         root  /opt/htdocs;
  15.     }
  16.     location ~ .*\.php?$
  17.     {
  18.         include conf/fcgi.conf;
  19.         fastcgi_pass  127.0.0.1:10080;
  20.         fastcgi_index index.php;
  21.     }
  22. }

当然也可以修改nginx源代码来解决
修改源代码,重新编译安装nginx
编辑nginx源代码

[root@localhost ~]# vim src/http/modules/ngx_http_static_module.c

修改: 找到下面一段注释掉

  1. /*
  2. if (r->method & NGX_HTTP_POST)
  3. {
  4.     return NGX_HTTP_NOT_ALLOWED;
  5. }
  6. */

然后按照原来的编译参数,重新编译安装nginx,即可!

09/27/13

nginx虚机多域名,server_name问题

一个nginx虚机,可以绑定多个server_name,比如:

server {
listen       80;
server_name  www.phpdba phpdba.com ;
access_log logs/phpdba_access.log;
if ($host != ‘www.phpdba.com’) {
rewrite ^/(.*)$ http://www.phpdba.com/$1 permanent;
}
然而,server_name顺序不同,php程序获取到的$_SERVER["SERVER_NAME"]和getenv(‘SERVER_NAME’)获取服务器域名不一样。

* $_SERVER["SERVER_NAME"]或getenv(‘SERVER_NAME’)获取的始终将是Nginx server_name配置中的第一个域名,在程序开发中需要注意。这第一个域名就相当于Apache虚拟主机配置中的ServerName,后面的域名就相当于Apache的ServerAlias。

Nginx 内部重定向规则启动时,

例如:当 URL 指向一个目录并且在最后没有包含“/”时,Nginx 内部会自动的做一个 301 重定向,这时会有两种情况:
1、server_name_in_redirect on(默认),URL 重定向为: server_name 中的第一个域名 + 目录名 + /;
2、server_name_in_redirect off,URL 重定向为: 原 URL 中的域名 + 目录名 + /。

当你有多个域名要指向同一个虚拟主机,并且你自己写 301 重定向规则把它们合并到某一个域名时,情况就更复杂了:
首先,nginx 检查 URL,如果符合条件,就用该规则(你写的)做第一遍重定向,接着,检查新生成的 URL,如果符合内部自动重定向之条件,就用前面提到的规则再做一次重定向。

至于 PHP 的 $_SERVER["SERVER_NAME"],在 nginx 中默认是由 nginx 的变量 $server_name 提供,这时它和重定向没有关系,始终是 server_name 设置中的第一个域名,但这是可以被改变的,在你的 nginx 配置中找到 fastcgi_param 部分,修改
fastcgi_param  SERVER_NAME    $server_name;

fastcgi_param  SERVER_NAME    $host;
但现在就要注意了,此时的 $_SERVER["SERVER_NAME"] 会受你写的和 nginx 自己的重定向规则所影响而变化。

1、设置 fastcgi_param  SERVER_NAME    $host;
2、设置 server_name_in_redirect off; 让 nginx 在处理内部重定向时,不使用 server_name 设置中的第一个域名;
3、不要使用 nginx 的 rewrite 规则来重定向、合并多个域名。

09/18/13

nginx内核参数优化

Nginx内核参数的优化:

net.ipv4.tcp_max_tw_buckets = 6000

timewait的数量,默认是180000。

net.ipv4.ip_local_port_range = 1024 65000

允许系统打开的端口范围。

net.ipv4.tcp_tw_recycle = 1

启用timewait快速回收。

net.ipv4.tcp_tw_reuse = 1

开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接。

net.ipv4.tcp_syncookies = 1

开启SYN Cookies,当出现SYN等待队列溢出时,启用cookies来处理。

net.core.somaxconn = 262144

web应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn限制到128,而Nginx内核参数定义的NGX_LISTEN_BACKLOG默认为511,所以有必要调整这个值。

net.core.netdev_max_backlog = 262144

每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。

net.ipv4.tcp_max_orphans = 262144

系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。如果超过这个数字,孤儿连接将即刻被复位并打印出警告信息。这个限制仅仅是为了防止简单的DoS攻击,不能过分依靠它或者人为地减小这个值,更应该增加这个值(如果增加了内存之后)。

net.ipv4.tcp_max_syn_backlog = 262144

记录的那些尚未收到客户端确认信息的连接请求的最大值。对于有128M内存的系统而言,缺省值是1024,小内存的系统则是128。

net.ipv4.tcp_timestamps = 0

时间戳可以避免序列号的卷绕。一个1Gbps的链路肯定会遇到以前用过的序列号。时间戳能够让内核接受这种“异常”的数据包。这里需要将其关掉。

net.ipv4.tcp_synack_retries = 1

为了打开对端的连接,内核需要发送一个SYN并附带一个回应前面一个SYN的ACK。也就是所谓三次握手中的第二次握手。这个设置决定了内核放弃连接之前发送SYN+ACK包的数量。net.ipv4.tcp_syn_retries = 1

在内核放弃建立连接之前发送SYN包的数量。

net.ipv4.tcp_fin_timeout = 1

如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。对端可以出错并永远不关闭连接,甚至意外当机。缺省值是60 秒。2.2 内核的通常值是180秒,你可以按这个设置,但要记住的是,即使你的机器是一个轻载的WEB服务器,也有因为大量的死套接字而内存溢出的风险,FIN- WAIT-2的危险性比FIN-WAIT-1要小,因为它最多只能吃掉1.5K内存,但是它们的生存期长些。

net.ipv4.tcp_keepalive_time = 30

当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时。

09/17/13

nginx upstream的排错逻辑

一、死循环

当设定proxy_next_upstream http_404,并且upstream里配置是max_fails=0,则访问到不存在文件时,将会有死循环现象,nginx负载明显升高,kill -HUP进程消逝很慢。同样的,proxy_next_upstream如果设定为其它值,例如http_503、invalid_header等也会有同样问题,但默认的error timeout不会出问题。

二、502 bad gateway

nginx的检测后端机制相对严格,默认的,某个后端一个失败的请求,就会令此后端暂停10秒。一般而言,不会有太多失败的请求,而且也不太可能所有机器在10秒内同时出问题;但世事总是难料,nginx->nginx的架构,常因后台服务器文件有错(通常是ssi),报出莫名其妙的错误,导致代理挂起此后端,然后proxy_next_upstream又将此请求导向下一台服务器,因此在10秒内,所有服务器都将被挂起……

解决此问题,最直接的办法是将max_fails设为0,关闭nginx屏蔽后端的功能,虽然有可能会造成死循环,但死循环还不至于很快停止服务,只是负载较高,至少可以挺一阵子。后端的错误如影响太大,还是要及时解决。

三、timeout

如果设定了max_fails=0,则某后端出了故障造成访问超时时不会被屏蔽,进而访问到此后端的请求都要等到超时才会跳到下一个后端,虽然请求最终还是成功了,但用户体验会变得很差。解决此问题的办法就是将超时时间调得很短,大概3-15秒,只要在用户(老板)能接受的范围内即可。