魏长东

weichangdong

nginx常见问题处理

nginx配置中各个作用域
nginx.conf主要且常用的四个作用域http,server,location,if。
……
http {
…..
include       mime.types; 
client_max_body_size 25600k;
client_body_buffer_size 25600k;
fastcgi_buffer_size 25600k;
fastcgi_buffers 4 25600k;
fastcgi_busy_buffers_size 51200k;
fastcgi_temp_file_write_size 51200k;
 
#后端负载均衡配置
#include upstream.conf
upstreamserver_xxx_backends { 
server 10.26.101.142:8080 max_fails=2 fail_timeout=1s;
server 10.26.101.143:8080 max_fails=2 fail_timeout=10s;
…...
}
 
#pc虚拟机配置
#include conf/vhost/xxx.yyy.com
server {
listen 8080;
server_name  xxx.yyy.com;
 
if ($request_uri ~ "xxx") {
rewrite "xxx" "yyy" break;
}
 
location ~ "^/taskui/" {
proxy_passhttp://server_xxx_backends;
 
}
location ~ "^/static/" {
root '/root/nginx/webroot';
indexindex.php
}
location ~ / {
access_log  logs/access_log;
fastcgi_pass 127.0.0.1:6666
 
if ($request_uri ~ "xxx") {
rewrite "xxx" "yyy" break;
}
}
…..
}
 
#wap虚拟机配置
#include conf/vhost/xxxwap.yyy.com
server {
listen 8080;
server_namexxxwap.yyy.com;
…..
}
…...
}

问题定位与解决方案

1,nginx超时、缓冲配置
Nginx    lighttpd
•    proxy_connect_timeout 1s;
•    proxy_send_timeout  720s;   ->12min
•    proxy_read_timeout 3600s;  ->60min     •    server.read-timeout = 720
•    server.write-timeout = 3600
•    #
•    server.max-keep-alive-idle = 30
•    server.max-read-idle = 360
•    server.max-write-idle = 360
•    server.max-connection-idle = 6
 
•    fastcgi_buffer_size 64k;     #这里可以设置为fastcgi_buffers指令指定的缓冲区大小
•    fastcgi_buffers  4 64k;        #指定本地需要用多少和多大的缓冲区来缓冲FastCGI的应答
•    fastcgi_busy_buffers_size 128k;        #建议为fastcgi_buffers的两倍
•    fastcgi_temp_file_write_size 128k;   #在写入fastcgi_temp_path时将用多大的数据块,默认值是fastcgi_buffers的两倍,设置上述数值设置太小时若负载上来时可能报 502 Bad Gateway
 
【问题】文库上传大附件,buffer不够用导致频繁的IO。
如果buffer设置过小会出现如下日志,即将cgi返回内容写入临时文件:
2013/04/07 20:41:38 [warn] 606#0: *22511873 an upstream response is buffered to a temporary file/root/nginx/nginx_trans/cache/proxy/7/01/0000000017 while reading upstream, client: 61.158.237.77, logid: 2496893729,  tracecode: 24968937291966362890040720,  server: *.yyy.com,  request: "GET /play/bd286aef0975f46527d3e1c5?pn=151&rn=10 HTTP/1.0",  -,  upstream: "http://10.36.84.22:8099/browse/play/bd286aef0975f46527d3e1c5?pv=play&pn=151&rn=10",  host: "ai.xxx.yyy.com",  referrer: "http://www.yyy.com",  -,  -,  -,  -,
 
【解决】调整fastcgi buffer大小,线上配置:
•    client_max_body_size 25600k;
•    指定可接受客户端的最大body size,如果超出这个长度nginx返回413("Request Entity Too Large")
•    client_body_buffer_size 25600k;
•    这个指令指定客户端请求的中的body size使用多大的buffer,如果实际的body size超出buffer的大小,这时候将会实现“临时文件”。
•    fastcgi_buffer_size 25600k;
•    用于指定读取FastCGI应答第一部分需要多大的缓冲区,这个值表示将使用1个64KB的缓冲区读取应答的第一部分(应答头),可以设置为fastcgi_buffers选项指定的缓冲区大小。
•    fastcgi_buffers 4 25600k;
•    指定本地需要用多少和多大的缓冲区来缓冲FastCGI的应答请求。如果一个PHP脚本所产生的页面大小为 256KB,那么会为其分配4个64KB的缓冲区来缓存;如果页面大小大于256KB,那么大于256KB的部分会缓存到fastcgi_temp指定的 路径中,但是这并不是好方法,因为内存中的数据处理速度要快于硬盘。一般这个值应该为站点中PHP脚本所产生的页面大小的中间值,如果站点大部分脚本所产 生的页面大小为256KB,那么可以把这个值设置为“16 16k”、“4 64k”等。
•    fastcgi_busy_buffers_size 51200k;
Limits the total size of buffers that can be busy sending a response to the client while the response is not yet fully read
当一个cgi的返回还没有完全读完并且能快速向客户端返回请求时,所使用的最大busy buffer大小,如果有必要的话还会使用temp_file(下面的参数配置),默认值是fastcgi_buffers的两倍。
•    fastcgi_temp_file_write_size 51200k;
•    表示在写入缓存文件时使用多大的数据块,默认值是fastcgi_buffers的两倍。
 
2,虚拟主机
              2.1 与lighttpd相比语法更简单。
nginx    lighttpd
server{
server_namewk.yyy.com;
}
 
server {
server_nameapp.yyy.com;
}
 
server {
server_namexxx.yyy.com;
}     
$HTTP["host"] =~ "wk.yyy.com|wapxxx.yyy.com|…." {
      # wk.yyy.com
} else $HTTP["host"] =~ "appwk.yyy.com|..." {
      # appwk.yyy.com
} else {
      # xxx.yyy.com
}
 
【注意】nginx会将没有匹配到的虚拟主机使用第一个server的配置。
如xxx.bdimg.com,doc.yyy.com等上面的配置会导致doc.yyy.com走wk.yyy.com域名,但这不是服务想要的
【解决方案1】线上的第一个server{}是vhost/xxx.yyy.com:

【解决方法2】使用default指令
listen 80 default;
此种方式在配置项中体现的更清晰直接。
推荐使用方法2。
 
 
 
3,nginx日志
   3.1  access_log信息更详细
$upstream_status:  代理后端时,后端返回的http状态码,包括重试是否成功:一次成功retryN_status:200或两次成功retryN_status:504, 200等.
${request_time}s: 处理请求的耗时,其单位是s,但可以精确到毫秒。
日志如下:
27.213.175.92 - - [17/Apr/2013:16:10:37 +0800] "GET/view/ec8d2427aaea998fcc220e53.html HTTP/1.1" 200 28914 "…" "Mozilla/4.0 (...)" 27.213.175.92 nginx_trans_logid:0635698549 retryN_status:504, 2001.364s
 
   3.2  rewrite_log on可以跟踪每一个请求进行了多少rewrite的条件判断
此配置项会在线下测试时启很大的作用,特别是在rewrite迁移过程中。由于打rewrite日志会产生性能问题,所以线上将此配置项关闭。
 
 rewrite规则:
rewrite ".*" "/index.php?pv=tagsearch&$query_string?" break;
日志如下:
2013/04/01 15:44:04 [notice] 5948#0: *1313 rewritten data: "/index.php", args: "pv=tagsearch&", client: 172.22.170.32, -,  -,  server: *.yyy.com,  request: "GET /tagsearch?n=1&m=1 HTTP/1.1",  -,  -,  host: "xxx.yyy.com",  -,  -,  -,  -,
 
 
4,upstream.conf参数
    4.1  fail_timeout为封禁后端的时间,当后端一台upstream中的机器宕机,nginx接下来的fail_timeout时间内不会将请求转发给这台机器。
    4.2  weight权重,可以通过此参数将后端性能高的机器转发更多的流量。
upstreamserver_xxx_backends { 
server 10.26.101.142:8080 max_fails=2 fail_timeout=1s   weight=3;
server 10.26.101.143:8080 max_fails=2 fail_timeout=10s weight=1;
   …...
             }
 
nginx在何时进行next upstream,由proxy_next_upstream参数控制。
•    error — an error has occurred while connecting to the server, sending a request to it, or reading its response;
在连接后端机器或在读写过程中出现异常错误。
•    timeout — occurred timeout during the connection with the server, transfer the request or while reading response from the server;
当出现连接超时或读写超时。
•    invalid_header — server returned a empty or incorrect answer;
•    http_500 — server returned answer with code 500
•    http_502 — server returned answer with code 502
•    http_503 — server returned answer with code 503
•    http_504 — server returned answer with code 504
•    http_404 — server returned answer with code 404
•    off — it forbids the request transfer to the next server
 
 
5,nginx的rewrite 规则
【问题】
5.1  rewrite和location的顺序问题
sever区块中如果有包含rewrite规则,则会最先执行,而且只会执行一次, 然后再判断命中哪个location的配置,再去执行该location中的rewrite, 当该location中的rewrite执行完毕时,rewrite并不会停止,而是根据rewrite过的URL再次判断location并执行其中的配置. 那么,这里就存在一个问题,如果rewrite写的不正确的话,是会在location区块间造成无限循环的.所以nginx才会加一个最多重试10次的上限,如:
 
location/download/{
  rewrite  ^(/download/.*)/media/(.*)\..*$  $1/mp3/$2.mp3  last;
}
如:/download/xxx/media/name.wmv -> /download/mp3/name.wmv
 
 
5.2   rewrite中last和break最大的不同在于
- break是终止当前location的rewrite检测,而且不再进行location匹配
- last是终止当前location的rewrite检测,但会继续重试location匹配并处理区块中的rewrite规则
 
 
5.4     rewrite匹配部分不能匹配query_string部分的内容。
 
5.3     nginx rewrite会自动增加$query_string
if ($request_uri ~ "^/view/(\w+)(\.html)?(\?(.*))?"){
rewrite "^/view/(\w+)(\.html)?(\?(.*))?" "/browse/view/$1?pv=view&doc_id=$1&$query_string&url=$request_uri?" last;
等于:
rewrite "^/view/(\w+)(\.html)?(\?(.*))?" "/browse/view/$1?pv=view&doc_id=$1&url=$request_uri" last;
}
 
5.4  解决不支持嵌套if的问题
【问题】
nginx仅支持简单的if语法,如:if  (只能是一个条件){这里不能再写if语句}。
nginx不支持像if (…&&...) {} 或 if(){ if(){} }的语法。
【解决】通过set变量变向解决
            set $ua "";          #初始化变量,不然日志会打warning
if ($http_user_agent ~ "iPad"){
set $ua "iPad";
            }
if ($request_uri~ "^/static/manifest/app(.*)\.manifest"){
set $ua "${ua}404";
            }
if ($ua = "iPad404"){
rewrite ".*" "/error-404.php" break;
            }
 
6,请求一个目录时nginx不会自动访问 此目录下的index.html
【问题】访问非root定义的目录,不会像lighttpd自动访问index.html
如访问专题页:http://xxx.yyy.com/topic/gaokaozhuantijuheyemian
需要给redirect成:http://xxx.yyy.com/topic/gaokaozhuantijuheyemian/,即在末尾加"/",
再rewrite到$1/index.html,因为只有先追加“/”,index.html中的“../xxxx.css”才会找到正确的文件。
【线上实例】
localtion / {
root/root/nginx/docui/webroot;
indexindex.php index.htm index.html;
….
set $is_static "";
if (-d $request_filename){
set $is_static "o";
        }
if ($uri !~ "/$"){
set $is_static "${is_static}n";
        }
if ($is_static = "on"){
rewrite  "(.*)" "$uri/" redirect;
        }
if (-f $request_filename/index.html){
set $is_static "${is_static}k";
        }
if ($is_static = "ok"){
rewrite  "(.*)$" "$1/index.html" break;
        }
}
 
7,静态文件单独location
【问题1】Content-Type原本应为image/jpeg,但浏览器得到的是text/html
  nginx09061.png
 
【原因】在location中配置fastcgi_pass后,nginx会将请求转给cgi解释执行,cgi返回内容的Content-Type为text/html。
【解决】删除fastcgi_pass
删除fastcgi_pass后nginx直接返回静态文件的内容和类型。
即,nginx需要将静态文件单独一个location并且location中不能配置fastcgi_pass项。如下:
location ~ "^/static/" {
fastcgi_pass    127.0.0.1:6666;           #删除此配置项
}
 
 
总结:在nginx替换过程中,消耗精力最大的是rewrite规则,rewrite与业务逻辑最密切,所以在替换过程中需要严谨的调研和测试rewrite各个参数的作用的,其它参数的调整和配置项可以发挥服务器的更好的性能和解决一些迁移过程中配置项的逻辑问题。nginx最强大的功能是在代理后端时对http头部进行特殊的设置,这是lighttpd不及的。