分类目录归档:nginx

nginx 嵌套 error_page

nginx有时候希望嵌套的处理error_page
比如 location / {
error_page 404 @fetch
}

location @fetch {
error_page 404 @fetch2
proxy_pass http://backend1/one/;
}
location @fetch2 {
proxy_pass http://backend2/two/;
return 200 “xxx”;
}

这是个简单的例子,如果需要实现功能需要两个参数: proxy_intercept_errors on; recursive_error_pages on;
syntax: recursive_error_pages on | off;
default:
recursive_error_pages off;
context: http, server, location
Enables or disables doing several redirects using the error_page directive. The number of such redirects is limited.

syntax: proxy_intercept_errors on | off;
default:
proxy_intercept_errors off;
context: http, server, location
Determines whether proxied responses with codes greater than or equal to 300 should be passed to a client or be redirected to nginx for processing with the error_page directive.

nginx lua 模块的一个bug

近期协助排查一个故障发现有50x错误,开error日志发现有malloc或者crash work process的记录

[emerg] 5309#0: *288 malloc(808334101) failed (12: Cannot allocate memory) while sending to client
[notice] 4579#0: signal 18 (SIGCHLD) received
[notice] 4579#0: worker process 5211 exited with code 0

这其实是lua模块导致的问题,在0.7.5之前,nginx lua module存在 ngx.req.clear_header 导致的内存溢出问题,需要升级到最新版
that is all,完毕

nginx gunzip filter 模块

介绍下igor职业打手Maxim Dounin写的一个gunzip模块

Gunzip module for nginx.
This module allows gunzipping responses returned with Content-Encoding: gzip
for clients that doesn’t support it. It may be usefull if you prefer to store
data compressed (to save space or disk/network IO) but do not want to penalize
clients without gzip support.

Note well: only responses with Content-Encoding set to gzip before this module
are handled (e.g. using “add_header Content-Encoding gzip;” isn’t enough as it
happens after). As of now only proxy and fastcgi are able to do so.

这个模块能针对不支持gzip编码的客户端,直接解压gzip格式的内容
好处:
1.跟源站直接请求压缩的内容,减少回源带宽,提高响应速度
2.只保留一份压缩的内容,减少缓存的大小,相同的cache能放更多的内容
配置格式比较简单:

Configuration directives:

gunzip (on|off)

Context: http, server, location
Default: off

Switches gunzip.

gunzip_buffers

Context: http, server, location
Default: 32 4k/16 8k

Specifies number and size of buffers available for decompression.

Usage:

location /storage/ {
gunzip on;

}

需要特别指出:
1.客户端不支持gzip编码,那么gunzip模块就返回解压的内容
2.客户端支持gzip编码,那么gunzip就自动不起作用,返回原始内容

某些特殊需求,比如addition_filter,我们知道这些filter在非压缩的内容才能正常工作,要结合这个模块就不是那么方便了
因此可以简单修改代码,让它不理会客户端的header,总是返回非压缩内容,注释掉这一整块即可:
ngx_http_gunzip_filter_module.c

141 #if (nginx_version >= 8025 || (nginx_version >= 7065 && nginx_version < 8000)) 142 143 r->gzip_vary = 1;
144
145 if (!r->gzip_tested) {
146 if (ngx_http_gzip_ok(r) == NGX_OK) {
147 return ngx_http_next_header_filter(r);
148 }
149
150 } else if (!r->gzip_ok) {
151 return ngx_http_next_header_filter(r);
152 }
153
154 #else
155
156 if (ngx_http_gzip_ok(r) == NGX_OK) {
157 return ngx_http_next_header_filter(r);
158 }
159
160 #endif

有人写了个gunzip_always的开关补丁,可以控制这个属性,patch暂时不放出来

模块地址: http://mdounin.ru/hg/ngx_http_gunzip_filter_module/

nginx ssl https 设置

本文已经过时并且存在大量不安全,请参阅最新文档   https://www.4os.org/index.php/category/https/
nginx 默认编译就是支持https的,只需要开启ssl就好
配置如下:
                listen                  443 ;
                server_name             4os.org *.4os.org;

                ssl                     on;
                ssl_certificate         gz.crt;
                ssl_certificate_key     gz.key;
                ssl_session_timeout     5m;
                ssl_protocols           SSLv2 SSLv3 TLSv1;
                ssl_ciphers             ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
                ssl_prefer_server_ciphers       on;
....

其中:
gz.crt是证书颁发机构给的证书(免费ssl-证书/)
gz.key是解密后的私钥

上文中提到的私钥是加密的,可以在startssl的工具箱里边解密,也可以自己做:

openssl  rsa -in gz.pri -out gz.key,输入私钥生成时设置的密码,出来的就是不加密的私钥了,nginx启动也不会要你输入密码了

补充:

1. “SSL_CTX_use_PrivateKey_file fail”之类的错误,通常都是私钥和证书不匹配造成的,请确认你生成证书与私钥匹配

2.firefox证书需要根证书信息:

wget http://www.startssl.com/certs/sub.class1.server.ca.pem

cat sub.class1.server.ca.pem >> gz.crt

停止和重启:nginx与apache的不同

停止和重启apache与nginx有些许不同,彼此经验不能照搬

TERM:

两者相同,都是发指令给父进程,父进程立刻尝试杀死所有的子进程并退出

USR1:

nginx的文档说得很简单:reopen the logfile,实际上的操作是master重新打开日志文件,并改变日志文件权限,是worker进程有读写权限,然后发USR1给worker进程重新打开日志文件,这完全不涉及任何worker进程的重新启动

apache则不同,它的父进程会”建议”所有子进程完成当前请求后退出,父进程将重新读取配置文件和日志文件,每个子进程退出后父进程将生成新的子进程

HUP:

nginx收到这个信号会做3个事情:

1.重新读取配置文件

2.使用新的配置启动新的worker进程

3.旧的worker完成当前请求后退出

apache的做法不同,父进程接收到该信号后会跟TERM信号杀掉所有子进程,重新读取配置文件,重新打开日志文件,并声称新的子进程来服务.与TERM信号不同的是,父进程不退出,服务不会中止

nginx php 缓存

这边一直有个php cache的应用,原理是发起一个对自身的请求,保存成静态文件
之前这个应用在apache worker模式跑得很好,换到nginx的fast-cgi 后一直不是很正常

这个问题跟spawn-fcgi的工作原理有关: spawn-fcgi起N个进程,然后FIFO排队处理请求
当有并发>N个PHP CACHE的应用请求过来的时候,php cache的应用会再对自身发起请求,这个请求排在了这些请求的后边,而实际上不会有新的进程来处理这些请求,从而形成死锁,日志中会充斥”upstream timed out” “no live upstreams”

因此,工作模式的排队理论本身决定了这个故障不可避免

解决办法:使用nginx proxy_cache 绕开这个问题,把php cache这个功能交给proxy_cache来完成,nginx的work process遇到阻塞等待的情况,会把这个请求的工作sleep一段时间处理接下来的请求,这个特点明显优于spawn-fcgi的行为

1.建立 /cache/打头的可缓存php location

location ~* ^/cache/(.*\.php)${

}

2. 设置相关的proxy_cache:

1) CACHEZONE:

proxy_cache_path /dev/shm/cache/app.gd.sohu.com levels=1:2 keys_zone=default:100m max_size=4g inactive=20m;

2) CACHE目录:

proxy_read_timeout 5s;
proxy_connect_timeout 5s;
proxy_set_header Host $host;
proxy_cache_use_stale updating;
proxy_cache_key “$host$uri$is_args$args”;
proxy_cache default;

proxy_ignore_headers “Cache-Control”;
proxy_hide_header “Cache-Control”;

proxy_ignore_headers “Expires”;
proxy_hide_header “Expires”;

proxy_hide_header “Set-Cookie”;
proxy_ignore_headers “Set-Cookie”;

add_header Cache-Control max-age=60;

proxy_cache_valid 200 3m;
proxy_cache_valid any 0m;
proxy_temp_path /dev/shm/cache/tmp;

proxy_pass http://serverIP/$1$is_args$args;

3) PURGE设置:

location ~* ^/purge/(.*\.php)$ {
access_log logs/purge_app_access.log sohu;

allow 10.10.0.0/16;
deny all;
set $purge_key “$host/$1$is_args$args”;
proxy_cache_purge default $purge_key;

}

3.需要注意的地方:


1) 影响动态应用能否cache的有几个header:一般是”X-Accel-Redirect”, “X-Accel-Expires”, “Expires” or “Cache-Control” ,跟某些文档提到的cookie是没有关系的,我这里根据自己的应用ignore了 “Expires” 和”Cache-Control” 的header
2) 需要注意,php使用cookie(session也是cookie header实现的一种)来识别用户,如果是私密应用,需要把cookie加入cachekey里边,开放式应用则不必如此
3)设置完毕后可以在访问路径的前边加上/cache/实现php内容的缓存,再加上/purge/就能清除其缓存
4) 需要特别说明,我这边的版本是0.7系列,从测试看1.x系列是会forward cookie的,所以需要抹掉:

proxy_hide_header Set-Cookie;
proxy_ignore_headers Set-Cookie;

nginx limit zone and limit requests

今天刚好有人提起limit zone 和limit request 两个模块的问题,这里就顺便讲讲个人的一些看法:

1.limit zone:

This module makes it possible to limit the number of simultaneous connections for the assigned session or as a special case, from one address.

这个模块能针对指定的session限制并发连接数,比如限制来源地址这个特别例子

http {
limit_zone one $binary_remote_addr 10m;

server {
location /download/ {
limit_conn one 1;
}
}
}

这个配置针对 $binary_remote_addr 这个对象做了个10M的limitzone(limitzone用于存储这些变量用于判断是否超过了limit限制,这里是并发1)

2.limit request:

This module allows you to limit the number of requests for a given session, or as a special case, with one address

这个模块允许你限制指定session的请求数量,比如限制来源地址这个特别例子

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

server {
location /search/ {
limit_req zone=one burst=5;
}

看模块的说明不好懂,看这个配置就明白了,它是控制请求的频率,而不是并发的数量,单个IP burst并发进入请求是5,每秒请求限制是1

需要补充说明的是,这里的例子都是以来源IP来设置的,并非只能使用该变量.
其实指定设置其他的变量,也有不同的效果,比如limitzone,针对localtion 设置个静态变量,就相当于设置了某个路径的总体并发访问量了

朋友测试的报告地址是这个:http://storysky.blog.51cto.com/628458/642970
这个报告有什么问题呢:
1. 首先测试使用的是ab和test.php,这会导致无法正确的看到请求的结果(可以看到测试结果的503和200无规律分布)
2. 把”看来也不一定能限制的住1秒钟1个并发连接”和并发1个链接搞混了,limitzone是并发,不是频率,测试文件的size太小会导致看不出问题
因此,我这边做了个简单的DEMO说明下吧:
1. 测试文件使用了个3.9M的文件,避免第一个请求迅速完成导致并发数无法精确达到预期
2. 对location 限速,limit_rate 10k,理由也同上
测试情况:
1.第一个请求发起:

curl -H”Referer:http://www.sohu.com/” http://www.4os.org/video/other/bnx2.tar.gz -o /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
4 3927k 4 168k 0 0 11283 0 0:05:56 0:00:15 0:05:41 11931^C

2.在第一个请求未完成的时候发起第二个请求:毫无疑问的503了

curl -H”Referer:http://www.sohu.com/” http://www.4os.org/video/other/bnx2.tar.gz
503 Service Temporarily Unavailable…

至于limitRequests的测试和说明,那篇文章说得挺好的,burst的解释我也是看了之后才清楚的,表示感谢

PS.最近该把http://wiki.nginx.org/HttpLimitReqModulehttp://wiki.nginx.org/HttpLimitZoneModule
这两个模块的中文WIKI补上…

nginx 视频服务器 架设

1.web服务器选用,建议使用0.8的稳定版

2.nginx默认自带了flv解码,编译的时候加上参数–with-http_flv_module即可

3.比较流行的mp4编码,需要另外安装第三方模块,建议使用http://h264.code-shop.com的H246方案

这个模块最新版本是2.3.2(不是官网介绍的2.2.7),并且需要打patch fix两个bug

1)zero_in_uri在0.8系列取消

2)404  错误返回415

模块可以从 http://h264.code-shop.com/download/nginx_mod_h264_streaming-2.3.2.tar.gz 下载

补丁可以从   http://www.4os.org/video/nginx/h246_stream_2.3.2.patch 下载

(不建议使用2.2.7版本,该版本有fclose空指针漏洞,并根据迅雷给我的反馈看存在编码兼容性造成的高负载问题)

4. 编译参数,简单示例下:

./configure –with-http_flv_module  –add-module=/path_to/nginx_mod_h264_streaming-2.3.2

如果不想自己patch,这里有patch好的模块,一样可用:

h264_stream_2.3.2.tar.gz

5.防盗链/限速/IO优化等,将在后续详细讲解:

1)防盗链:防盗链分为简单的refer引用判断和较为复杂的动态链接两种(比如时间/文件名双因子加密)

2)限速:nginx天然的支持了限速,limit-rate参数不仅仅能让视频流平滑的输出,还能极大的缓解IO压力

3)IO优化:包括内核调整/磁盘raid设置/使用SSD设备,在具备优秀开发人员的情况下甚至可以打散磁盘放置多份热点等

any question,mail to favortel#qq.com

Nginx启动初始化过程(转载)

main函数做的第一件事情就是对参数选项进行处理,和普通的Linux程序如出一辙,如下:

if (ngx_get_options(argc, argv) != NGX_OK) { return 1; }

Nginx用此函数对参数选项进行解析,从而采取相应的动作,比如:显示版本、测试配置等功能。其实此函数实现的很简陋,远没有Linux提供的getopt()那么强悍,但却可以达到跨平台的目的。

     ngx_time_init();
#if (NGX_PCRE)
    ngx_regex_init();
#endif
    ngx_pid = ngx_getpid();
    log = ngx_log_init(ngx_prefix);
    if (log == NULL) {
        return 1;
    }

上述几行代码的功能如其名,主要完成Nginx在时间和日志等方面的初始化工作。

    if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK){
        return 1;
    }

将命令行参数保存到ngx_os_argv、ngx_argc以及ngx_argv这几个全局的变量中。这算是一个备份存储,方便以后master进程做热代码替换之用。

     if (ngx_os_init(log) != NGX_OK) {
        return 1;
    }

完成操作系统的一些信息获取,如内存页面大小、系统限制资源等信息;所有的这些资源都将会被保存在对应的全局变量中,因此后续访问将会很便利。

    if (ngx_crc32_table_init() != NGX_OK) {
        return 1;
    }

初始化一个做循环冗余校验的表,由此可以看出后续的循环冗余校验将采用高效的查表法。crc算法此处就不做分析,网上一堆一堆的相关资料,有兴趣的同学可以了解。

    if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {
        return 1;
    }

通过环境变量NGINX完成socket的继承,继承来的socket将会放到init_cycle的listening数组中。在NGINX环境变量中,每个socket中间用冒号或分号隔开。完成继承同时设置全局变量ngx_inherited为1。

    ngx_max_module = 0;
    for (i = 0; ngx_modules[i]; i++) {
        ngx_modules[i]->index = ngx_max_module++;
    }

额!!!这个循环中的ngx_modules数组好像没见定义,难不成是火星来的?当然不是,如其名,这就是一个存储所有模块的信息,包括自己开发的模块都会放到这个数组中,而这个神秘的数组却是在自动编译的时候生成的,位于objs/ngx_modules.c文件中。这个循环的目的是清晰可见的——对所有模块进行索引编号,方便以后访问;同时借助ngx_max_module对所有模块进行了一次点数,确定究竟有多少模块。而神秘数组ngx_modules的长相大概如下:

ngx_module_t *ngx_modules[] = {
    &ngx_core_module,
    &ngx_errlog_module,
    &ngx_conf_module,
    &ngx_events_module,
    &ngx_event_core_module,
    &ngx_epoll_module,
    &ngx_openssl_module,
    &ngx_http_module,
    。。。
};
    cycle = ngx_init_cycle(&init_cycle);

这里将会初始化很多的东东到全局变量cycle中,是Nginx启动初始化的核心之处。ngx_init_cycle函数的过程比较多,放下一篇blog中逐段分析。

    if (ngx_init_signals(cycle->log) != NGX_OK) {
        return 1;
    }

注册一堆信号处理程序,需要注册的信号及相应的信号处理函数被放在一个类型为ngx_signal_t的数组signals中。数组定义在src/os/unix/ngx_process.c中。ngx_signal_t结构类型定义了信号值,信号名字,信号对应动作名以及信号处理函数。

     if (!ngx_inherited && ccf->daemon) {
        if (ngx_daemon(cycle->log) != NGX_OK) {
            return 1;
        }
        ngx_daemonized = 1;
    }

ngx_daemon肯定就是用来实现守护进程的函数了,此处就不多废话了,有需要写server程序的,可以直接copy这段代码实现守护进程。

    if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) {
        return 1;
    }

玩过Nginx的人都知道,Nginx启动后有一个记录进程id的文件,这个文件里面就一个pid。原来这个pid就是在这个地方记录下来的。查看ngx_create_pidfile函数可以看到这样的一行代码

if (ngx_process > NGX_PROCESS_MASTER) {
return NGX_OK;
},这行代码就说明了,不是master进程时,就不创建这样的一个pid文件。

    if (ngx_process == NGX_PROCESS_SINGLE) {
        ngx_single_process_cycle(cycle);
    } else {
        ngx_master_process_cycle(cycle);
    }

到此就基本完成Nginx的启动初始化过程了,即将开始进程相关的工作了,这里最重要的ngx_master_process_cycle这个过程,在这个过程里实现了master-worker模式的进程模型,也是生成环境下Nginx的常用模型。既然此处已不再是初始化工作,那么就留到后续分析吧。

上述所有过程都是在main函数里完成的,下一篇分析ngx_init_cycle函数。

From: http://www.tbdata.org/archives/1092

nginx core dump trace

问题: 如何core dump

答案: 配置方法如下

1. 配置nginx跑在非daemon和非master_process模式,配置样例

daemon off;

master_process off;

2. 配置worker_processes个数,可设置为1

worker_processes 1;

3. 启动nginx

ulimit -c unlimited;

/opt/soft/nginx/sbin/nginx -c /opt/soft/nginx/conf/nginx.conf;

4. 阅读coredump

gdb sbin/nginx  -c core.$pid$

gdb>where

#貌似说最新的coredump办法,不影响在线业务

nginx配置方法
worker_rlimit_core  500M;
working_directory   /path/to/cores/;
要保证nginx进程用户对working_directory可写
原来的配置都不用动,也跑在daemon模式下,有master进程
只需要增加这两行就成了
worker_rlimit_core限制生成core大小的,如果占内存多就搞大