一般 nginx 搭配 php 都采用这样的方式:

location ~ \.php$ {
    proxy_pass http://localhost:9000;
    fastcgi_param SCRIPT_FILENAME /data/_hongdou$fastcgi_script_name;
    include fastcgi_params;
}

这个方式只能连接到一组 spawn-fcgi 开启的 fastcgi,在服务器负载稍高时常常出现 502 bad gateway 错误。

起先怀疑这是 php-cgi 的进程开得太少,增加后仍然有反映时常有错,偶然间发现 php-cgi 会报出这样的错误:

zend_mm_heap corrupted

看来是 php-cgi 在执行某些代码时有问题,以致于该线程中止。

在服务器上可能还会看到 php-cgi 进程在不断变少,估计是出现错误的 php-cgi 的进程自动退出了。

php 的问题总是不太容易能解决,所以在 nginx 方面想想办法,nginx 的好处是它总是能爆出一些稀奇古怪的做法出来。

nginxproxy 中,规避莫名其妙错误的办法无非是 proxy 到一个 upstream 的服务器组中,然后配置 proxy_next_upstream,让 nginx 遇到某种错误码时,自动跳到下一个后端上。这样,应用服务器即使不稳定,但是在 nginx 后面就变成了稳定服务。想到 nginxfastcgiproxy 是一路东西,所以 proxy 能用的经验,移植到 fastcgi 也能跑得起来。

照着这个思路,用 spawn-fcgi 多开同样一组 php 进程,所不同的仅仅是端口:

spawn-fcgi -a 127.0.0.1 -p 9000 -u nobody -f php-cgi -C 100
spawn-fcgi -a 127.0.0.1 -p 9001 -u nobody -f php-cgi -C 100

然后把 fastcgi 的这段配置改成用 upstream 的方式:

upstream backend {
    server 127.0.0.1:9000;
    server 127.0.0.1:9001;
}
location ~ \.php$ {
    fastcgi_pass backend;
    fastcgi_param SCRIPT_FILENAME /data/_hongdou$fastcgi_script_name;
    include fastcgi_params;
}

检查配置结果正确,能跑起来;同时在服务器上 netstat -n|grep 9000grep 9001 都有记录,证明连接无误;在前台查阅页面,一切运行正常。

这个配置是最简单的配置,既然能连接上 upstream,那么很显然 upstream 的一些东西都可以拿来用,比如 ip_hashweightmax_fails等。

这样的配置在单机下不知能不能共享 session,没有测试,如果有问题,可以加上 ip_hash,或者配置 phpsession 存进 memcached 中。

然后就是 fastcgi_next_upstream 的配置,nginx wiki 中没有介绍到这个配置,查了一下,在 nginxCHANGES 中有提到,而且出生年月是和 proxy_next_upstream 一样的。既然如此,那就照 proxy_next_upstream 一样配吧。一般按默认的值 error timeout 就可以工作,因为 php 出现 502 错误的异常是返回的 500 错误,所以我把 fastcgi_next_upstream 定为:

fastcgi_next_upstream error timeout invalid_header http_500;

通过这个配置,就可以基本杜绝任何时常性的 500 错误,出问题的几率会变小很多,如果客户反映仍然激烈,那么就多增加几组 fastcgi 进程。

以上配置能够杜绝由于 php 所引起的“莫名其妙”的时常性的 502 错误,同时可使 nginx 搭配 php 比从前方式更为强悍。假如 nginx 还是返回 502 错误,那这次就一定是出现服务器挂掉或其它严重问题的了。