了解基本原理
浏览器访问网页的过程
请求静态页面
Browser
请求http://xxx.com/aa.html
-> Web Server(Nginx/Apache)分发 -> 找到aa.html
文件返回给Browser
。
请求动态脚本
Browser
请求http://xxx.com/bb.php
-> Web Server(Nginx/Apache)分发 -> PHP解析器
(PHP-CGI
程序)-> 返回处理结果给Web Server -> 返回数据给Browser。
原理:服务器根据配置文件
,知道这是一个PHP
脚本文件,需要去找PHP解析器
来处理。
PHP解析器
会解析php.ini
文件初始化执行环境,然后处理请求,再以标准的数据格式返回处理结果,最后退出进程。
CGI 程序到 FPM 进化史
image
CGI(Common Gateway Interface)
CGI
是服务器与后台语言交互的协议,有了这个协议,开发者可以使用任何语言处理服务器转发过来的请求,动态地生成内容,保证了传递过来的数据是标准格式
的(规定了以什么样的格式传哪些数据(URL、查询字符串、POST数据、HTTP header等等
)),方便了开发者。
PHP-CGI(PHP CGI)
PHP语言
对应与服务器交互的CGI程序
就是PHP-CGI
。
CGI程序
本身只能解析请求
、返回结果
,不会进程管理
,所以有一个致命的缺点,那就是每处理一个请求都需要fork
一个全新的进程,随着Web
的兴起,高并发越来越成为常态,这样低效的方式明显不能满足需求(每一次web请求
都会有启动和退出进程
,也就是最为人诟病的fork-and-execute
模式,这样一在大规模并发下,就死翘翘了)。
就这样,FastCGI
诞生了,CGI程序
很快就退出了历史的舞台。
FastCGI(Fast CGI)
FastCGI
,顾名思义就是更快的CGI程序
,用来提高CGI程序
性能,它允许在一个进程内处理多个请求
,而不是一个请求处理完毕就直接结束进程,性能上有了很大的提高。
提高性能?那么CGI程序
的性能问题在哪呢?
PHP解析器
会解析php.ini
文件,初始化执行环境,就是这里了。
标准的CGI程序
对每个请求都会执行这些步骤(不闲累啊!启动进程很累的说!),所以处理每个请求的时间会比较长。这明显不合理嘛!
那么FastCGI
是怎么做的呢?
首先,FastCGI
会先启一个master进程
,解析配置文件,初始化执行环境,然后再启动多个worker进程
。当请求过来时,master
会传递给一个worker
,然后立即可以接受下一个请求。
这样就避免了重复的劳动,效率自然是高。
而且当worker
不够用时,master
可以根据配置预先启动几个worker
等着。
当然空闲worker
太多时,也会停掉一些,这样就提高了性能,也节约了资源。这就是FastCGI
的对进程的管理。
ps:也有一些能够调度PHP-CGI进程
的程序,比如说由lighthttpd
分离出来的spawn-fcgi
。好了,PHP-FPM
也是这么个东东,在长时间的发展后,逐渐得到了大家的认可(要知道前几年大家可是抱怨PHP-FPM
稳定性太差的),也越来越流行。
image
PHP-FPM(FastCGI Process Manager)
它是FastCGI协议
的一个实现,任何实现了FastCGI协议
的服务器都能够与之通信。
FPM
之于标准的FastCGI程序
,也提供了一些增强功能,具体可以参考官方文档:PHP: FPM Installation。
FPM
是一个PHP进程管理器
,包含master
和worker
两种进程。
master进程
只有一个,负责监听端口,接收来自服务器的请求,而worker进程
则一般有多个(具体数量根据实际需要配置),每个进程内部都嵌入了一个PHP解释器
,是PHP代码
真正执行的地方,下面是我本机上FPM
的进程情况:1个master进程
,2个worker进程
。
$ ps -ef | grep fpm
root 130 1 0 01:37 ? 00:00:01 php-fpm: master process (/usr/local/php/etc/php-fpm.conf)
php-fpm 131 130 0 01:37 ? 00:00:00 php-fpm: pool www
php-fpm 133 130 0 01:43 ? 00:00:00 php-fpm: pool www
从FPM
接收到请求,到处理完毕,其具体的流程如下:
FPM
的master进程
接收到请求。 master进程
根据配置指派特定的worker进程
进行请求处理,如果没有可用进程,返回错误,这也是我们配合Nginx
遇到502
错误比较多的原因。 worker
进程处理请求,如果超时,返回504
错误。 请求处理结束,返回结果。
FPM
从接收到处理请求的流程就是这样了,那么Nginx
又是如何发送请求给FPM
的呢?
这就需要从Nginx
层面来说明了。
我们知道,Nginx
不仅仅是一个Web服务器
,也是一个功能强大的Proxy服务器
,除了进行http请求
的代理,也可以进行许多其他协议请求的代理,包括本文与FPM
相关的FastCGI协议
。为了能够使Nginx
理解FastCGI协议
,Nginx
提供了FastCGI模块
来将http请求
映射为对应的FastCGI
请求。
Nginx
的FastCGI模块
提供了fastcgi_param指令
来主要处理这些映射关系
,下面 是Nginx
的一个配置文件实例,其主要完成的工作是将Nginx
中的变量翻译成PHP
中能够理解的变量。
$ cat /usr/local/nginx/conf/fastcgi.conf
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
除此之外,非常重要的就是fastcgi_pass指令
了,这个指令用于指定FPM进程
监听的地址,Nginx
会把所有的PHP请求
翻译成FastCGI请求
之后再发送到这个地址。下面一个简单的可以工作的Nginx配置文件
:
server {
listen 80;
server_name test.me;
root /usr/local/web/myproject/public;
index index.php index.html index.htm;
access_log /usr/local/nginx/logs/test-access.log;
error_log /usr/local/nginx/logs/test-error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~\.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/local/web/myproject/public/$fastcgi_script_name;
fastcgi_pass unix:/usr/local/php/var/run/php-fpm.sock;
fastcgi_index index.php;
}
}
在这个配置文件中,我们新建了一个虚拟主机
,监听80端口
,项目根目录
为 /usr/local/web/myproject/public
。然后我们通过location指令
,将所有的以.php结尾
的请求都交给FastCGI模块
处理,从而把所有的PHP请求
都交给了FPM
处理,从而完成Nginx
到FPM
的闭环。
如此以来,Nginx
与FPM
通信的整个流程应该比较清晰了。
image
修改了php.ini
配置文件后,使用PHP-FPM
为什么能平滑重启?
修改php.ini
之后,PHP-CGI进程
是没办法平滑重启的。
PHP-FPM
对此的处理机制是新的worker进程
用新的配置,已经存在的worker进程
处理完手上的活就可以歇着了,通过这种机制来平滑过渡
。
转发来源:http://www.voycn.com/article/lijie-nginx-yu-php-fpm-tongxindegongzuojizhi