nginx负载均衡和配置PHP-FPM

PHP开发基本上都知道两种运行环境,分别是LNMP和LAMP。区别主要在N和A上,当然从细节上来区分,两个P也有不一样的地方。不谈之前的浅淡理解,最近一个项目上要配合进行PHP服务器指向的切换。发现A服务器上安装了apache,B服务器上安装了nginx,每次请求B服务器的nginx,业务逻辑在A上执行。

这和我之前所了解到的不太一样,一般来说apache和php通过mod_php方式运行,而要是用到nginx,往往就直接用了php-fpm。在一番查看后发现,确实基本apache的mod_php进行PHP解析,通过访问B返回了正确的脚本执行结果,而直接访问了A,发现也得到了正确的结果。区别仅在于ip地址和端口的差别。

打开nginx的配置文件nginx.conf发现类似如下一段信息:

location /papa/ {
   proxy_pass http://192.168.3.100/;
}

proxy模块相关配置参数很多,proxy_pass简单来说就是做一遍HTTP请求转发。暂时不去深究为什么用了nginx,又用了apache。个人认为这种方式存在不足之处,nginx一直以来以其静态处理性能强劲闻名(当然现在动态处理能力也很强),通过配置location匹配抓取访问URL指定字段,再转发请求到apache服务器。可以看出来动态和静态请求都全部推给了apache,那么nginx仅仅就是做了个转发,HTTP转发这种事情,总会存在损耗。完全没有利用起来nginx强劲的效果,唯一的好处可能就是做了负载均衡,方便布置集群。

 

第一想聊聊,基于压测,要求部署两套PHP的执行环境。

location /papa/ {
  proxy_pass http://test_common/;
}

upstream test_common {
  server 127.0.0.1;
}

proxy_pass 配置转发请求地址,upstream配置多个server地址,且功能真的强大,举几个简单的例子。

普通轮询模式:按照1:1的比例轮询服务器执行脚本

upstream test_common {
   server 192.168.0.1;
   server192.168.0.2;
}

权重轮询模式:按照设置的比例轮询服务器执行脚本

upstream test_common {
   server 192.168.0.1 weight=3;
   server192.168.0.2 weight=2;
}

IP分配模式:根据请求的IP分配执行服务器,保证相同IP在一个服务器上执行脚本。

之前在发布到线上的时候,由于部署了两套服务器,导致基于SESSION的部分,出现问题,现在来看,一句话就能搞定。

upstream test_common {
   ip_hash;
   server 192.168.0.1 weight=3;
   server192.168.0.2 weight=2;
}

 nginx负载均衡和配置PHP-FPM_第1张图片

重启nginx,一访问报了个400的错误。

改动nginx.conf配置文件,访问出现400异常,那么暂时认定配置文件的改动问题,根据页面报错信息查错,过程比较持久和繁杂,也没有能解决的方法。

后来想想,继续是nginx配置后出错了,那就查查nginx的运行日志吧,进入到nginx根目录,执行tail -f logs/access.log

访问一次后,发现新增一行


实际上这是apache的返回内容,进行apache日志目录,查看日志

诶,发现确实有点搞头了,居然是apache报的错,结合之前的资料搜索,有粗略提到头部问题,就决定看看能不能抓到请求的头部信息来试试水了。

执行命令:tcpdump -i lo port 80 -s 1024 -l –A

Host是upstream定义的虚拟名称,nginx.conf改回初始配置继续抓包结果为:

同时设置apache日志级别为debug后,定位日志:

AH02415: [strict] Invalid host name'test_common', problem near: _commo

AH00550: Client sent malformed Host header:test_common

 

定位到是请求头Host的问题,可以使用proxy_set_header来设置Host信息

location /papa/ {
  proxy_pass http://test_common/;
  proxy_set_header Host $host; 
}

修改如上配置,继续调试,

抓取头部发现变更成了IP地址,同时请求执行不再报400错误。

根据结果验证过程,查了下nginx负载均衡相关,发现确实都有设置Host一项。

要特别说明的是,线上及以上重现为apache2.4和PHP5.6版本。后续在另外apache2.2和PHP5.3版本上测试时,发现不添加proxy_set_header Host $host;脚本执行成功,Host也确实异常。

 

apache2.4和apache2.2对HTTP请求Host的检测规则可能不一样?

两个版本造成不同结果的原因点,没找出来。

 

第二想聊聊nginx和php-fpm之间的配置。

php-fpm安装比较方便,基于5.3.3以上的版本已经被PHP集成,因此编译的时候加入--enable-fpm即可。

单纯的执行.php文件,配置非常方便,nginx.conf注释的内容打开简单修改就能直接用。

不过日常开发的项目都是基于TP框架,当然laravel原理也一样。隐藏了入口文件index.php,同时访问路由通过URL携带进行解析后执行。

if (!-e $request_filename) {
  rewrite  ^/(.*)$  /index.php/$2 last;
}

这个配置主要是为了对访问URL进行重写操作,如果不存在此文件,在最前端写入index.php,这和apache的.htaccess文件功能一样。last网上的说法是重新发起一个请求(这里存在疑问,重新发起请求是指新的一个HTTP请求吗?观察执行日志并没有两条请求,我个人感觉可以理解为,继续匹配后续的location了),相对的break会停止对后续location的匹配。

在经过URL重写后,要对php文件继续做location的匹配,当找到匹配,则发送请求到php-fpm。

光配置这个一个简单的rewrite,还碰到了一点槛,访问http://127.0.0.1/tp/Index/index提示Accessdenied.

观察nginx日志,提示/index.php/tp/Index/index has been denied。暂不考虑其他的,这个tp和index.php位置就不对,而apache重写的rewrite正则相似,但是能正确的写入index.php。

新改动如下:

if (!-e $request_filename) {
  rewrite  ^/(.*?)/(.*)$  /$1/index.php/$2  last;
}

重新的URL是正确了,因为没法使用域名来测试,域名包含了项目名,应该不会出现这种情况。

 

剩下就是对php文件location部分改动了,默认是

location ~ \.php$ {
   root           html;
   fastcgi_pass   127.0.0.1:9000;
   fastcgi_index  index.php;
   fastcgi_param SCRIPT_FILENAME  /scripts$fastcgi_script_name;
   include        fastcgi_params;
}

刚开始想着对正则改改,如:~ \.php(.*)$。毕竟index.php后面携带了内容嘛,后来想想有点傻,实际上并不存在类似tp/index.php/Index/index文件。


查找配置

location ~ \.php($|/) {
   include                fastcgi_params;
   fastcgi_pass           127.0.0.1:9000;
   fastcgi_index           index.php;
   set $path_info "/";
   set $real_script_name $fastcgi_script_name;
   if ($fastcgi_script_name ~ "^(.+?\.php)(/.+)$") {
        set $real_script_name $1;
        set $path_info $2;
    }
   fastcgi_param SCRIPT_NAME $real_script_name;
   fastcgi_param PATH_INFO $path_info;
   fastcgi_param SCRIPT_FILENAME $document_root$real_script_name;
}

和默认配置区别在于除了转发请求,还设置了脚本文件名、PATH_INFO变量(业务执行文件相关参数)、脚本文件绝对路径。这三个变量都能通过SERVER变量打印观察。

 

总结一下,其实对这一块的梳理还挺乱的,不过算是了解了下nginx和其与php-fpm之间的配置关系。其本质是一个代理服务器,在能够高效处理静态资源的同时,作为类似Web请求中转站和php-fpm建立联系,以sockt方式通信达到运行PHP文件的目的。

另外配置参数挺多,所以还是好好看看官方文档来做到知根知底吧。


你可能感兴趣的:(PHP)