解决nginx集成php虚拟目录问题

近期偶发少年狂,前天(3月3日)从北京大兴机场回深圳的途中,考虑到近期失眠比较厉害,三个小时的无所事事实在是无法接受。于是准备将本地web服务器从apache+mysql+php升级到nginx+mysql+php,说干就干,顺手下载了nginx的windows压缩包,百度了下教程就可以开工了。

设定目标为:

1、本地web服务器同时支持nginx和apache,保证两个应用服务一致

2、顺手解决请求特别慢的问题,目前打开wordpress首页需要6秒

理想是丰满的,现实是残酷的,遇到第一个问题是nginx和php-cgi均不支持注册服务,逐个点开在前台运行是不可接受的。还好想到了Windows支持“start /B”这种后台运行方式,勉强不算完美的解决了第一个问题。如果想彻底解决php-cgi.exe后台运行的问题,需要手工写一个windows service。

cd "E:\WebServer\PHP7(x64)"
start /B php-cgi.exe -b 127.0.0.1:9000 -c php.ini

cd "E:\WebServer\nginx"
start /B nginx.exe

遇到的第二个是问题是虚拟目录问题,当前结构为默认web应用主目录为E:/WebServer/wwwroot,Discuz部署在E:/WebServer/Discuz并使用/bbs访问,wordpress部署在E:/WebServer/wordpress/并使用/blog访问。

虚拟目录在apache支持的很好,nginx虽然支持alias,但是传递过来的请求必然携带了上下文,如果上下文和实际目录名不一致的情况下,导致传递进入php解析器的路径多了一层。

再次求助于百度,得到的解决方案为直接用rewrite干掉/bbs,按此思路改动nginx后,首页顺利打开了,但是静态资源包括其他PHP页面全部因为请求路径丢失了/bbs导致无法加载。

location /bbs/ {
    alias "E:/WebServer/Discuz/";
    index  index.html index.htm index.php;
}
location ~ ^/bbs/.+\.php$ {
    root E:/WebServer/Discuz;
    rewrite /bbs/(.*\.php?) /$1 break;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_split_path_info  ^(.+\.php)(/.+)$;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
}

再次求助于百度,网上一致的方案是对此类情况重新部署一个站点,不能通过带上下文的路径请求一个虚拟目录或者映射到一个站点的根目录。如果重新部署新站点,问题倒是很容易解决,但是与前期设定的目标不符。

仔细分析nginx调用php-cgi.exe的过程,结合php是解释语言,同时查看nginx的错误日志,发现无法解析主要是nginx传递到php-cgi.exe的路径不对导致的,默认路径为$document_root$fastcgi_script_name,其中$document_root为nginx中使用root或者alias指定的路径,$fastcgi_script_name为fastcgi_split_path_info后得到的php相对路径,现在主要问题为$fastcgi_script_name多了/bbs上下文,那解题思路就是通过改变这两个变量使得总体路径符合预期即可。

首先分析alias,此处指定后所有的静态路径均可以正常访问,故不做改动。对于请求上下文为/bbs的php解析请求,重新设定$document_root为E:/WebServer,然后重新设定一个变量使其等于/Discuz+$uri即可,简单修改nginx配置,实验效果比较理想,基本达到目的,按照此思路同步修改/blog上下文,最终nginx配置如下:

location /bbs/ {
    alias "E:/WebServer/Discuz/";
    index  index.html index.htm index.php;
}
location /blog/ {
    alias "E:/WebServer/wordpress/";
    index  index.html index.htm index.php;
}

location ~ \.php$ {
    root E:/WebServer/wwwroot;
    set  $php_script_name $uri;

    if ($uri ~ ^/bbs/(.+)){
        root E:/WebServer;
        set  $php_script_name /Discuz/$1;
    }
    if ($uri ~ ^/blog/(.+)){
        root E:/WebServer;
        set  $php_script_name /wordpress/$1;
    }

    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_split_path_info  ^(.+\.php)(/.+)$;
    fastcgi_param  SCRIPT_FILENAME  $document_root$php_script_name;
    include        fastcgi_params;
}

location / {
    root   E:/WebServer/wwwroot;
    index  index.html index.htm index.php;
}

遇到的第三个问题是PHPMyAdmin,这个比较奇特,原始的PHPMyAdmin位于E:/WebServer/wwwroot/PHPMyAdmin,另外使用/phpMyAdmin访问E:/WebServer/phpMyAdmin,其中phpMyAdmin目录中只有一个index.php用于集成登录跳转。第一个感觉是通过location指定/phpMyAdmin时nginx并不区分大小写,强行使用大小写会导致后续一堆问题,后来还是通过php解析时通过大小写匹配,区分不同的解析路径,在问题二的配置中加上以下配置即可。

##此时不可以通过/phpMyAdmin虚拟化目录E:/WebServer/phpMyAdmin,否则会导致无穷跳转
##location /phpMyAdmin/ {
##    alias "E:/WebServer/phpMyAdmin/";
##    index  index.html index.htm index.php;
##}

   if ($uri ~ ^/phpMyAdmin/(.+)){
        root E:/WebServer;
        set  $php_script_name /phpMyAdmin/$1;
    }

遇到的第四个问题比较好解决,就是通过/bbs、/blog、/phpMyAdmin都无法打开应用,这是因为虚拟路径指定为/bbs/,在默认web主目录又找不到以上路径,导致nginx报404,做下强制跳转即可。

rewrite ^/bbs$ /bbs/index.php permanent;
rewrite ^/PHPMyAdmin$ /PHPMyAdmin/index.php permanent;
rewrite ^/phpMyAdmin$ /phpMyAdmin/index.php permanent;
rewrite ^/blog$ /blog/index.php permanent;

遇到的第五个问题是php解析慢,切换到nginx后wordpress首页还是需要6秒,说明与web服务器无关,后来自己写了两个php页面进行同样的计算,一个连数据库获取一个表中的一个字段,另外一个不连数据库,结果两个页面执行速度相差整整六秒,说明性能消耗在mysql连接上。继续求助于百度,最终的解决方案为禁用本机的ipv6解析协议即可,通过修改注册表重启机器后,wordpress首页响应恢复到毫秒级,符合预期。

reg add "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters" /v DisabledComponents /t REG_DWORD /d 255 /f

其他的均是一些小问题,譬如Discuz和wordpress的伪静态化,就不一一赘述。

通过这次动手,虽然让十来年没更的本地web服务器用上了nginx,也充分说明理论和想法都很简单,真正行动往往会遇到各种困难。

发表评论

邮箱地址不会被公开。 必填项已用*标注