文档章节

PHP FastCGI进程管理器PHP-FPM的架构

eechen
 eechen
发布于 2015/12/08 21:52
字数 1833
阅读 11350
收藏 153


一个master进程,支持多个pool,每个pool由master进程监听不同的端口,pool中有多个worker进程.
每个worker进程都内置PHP解释器,并且进程常驻后台,支持prefork动态增加.
每个worker进程支持在运行时编译脚本并在内存中缓存生成的opcode来提升性能.
每个worker进程支持配置响应指定请求数后自动重启,master进程会重启挂掉的worker进程.
每个worker进程能保持一个到MySQL/Memcached/Redis的持久连接,实现"连接池",避免重复建立连接,对程序透明.
使用数据库持久连接时应该设置固定数量的worker进程数,不要使用动态的prefork模式.

@syaokun219@IM鑫爷 纠正,以下两句有误:
master进程采用epoll模型异步接收和分发请求,listen监听端口,epoll_wait等待连接.
然后分发给对应pool里的worker进程,worker进程accept请求后poll处理连接.
应该是:
master进程并不接收和分发请求,而是worker进程直接accept请求后poll处理.
master进程不断调用epoll_wait和getsockopt是用来异步处理信号事件和定时器事件.
这里提一下,Nginx也类似,master进程并不处理请求,而是worker进程直接处理,
不过区别在于Nginx的worker进程是epoll异步处理请求,而PHP-FPM仍然是poll.

如果worker进程不够用,master进程会prefork更多进程,
如果prefork达到了pm.max_children上限,worker进程又全都繁忙,
这时master进程会把请求挂起到连接队列backlog里(默认值是511).

1个PHP-FPM工作进程在同一时刻里只能处理1个请求.
MySQL的最大连接数max_connections默认是151.
只要PHP-FPM工作进程数不超过151,就不会出现连接不上MySQL的情况.
而且正常情况下,也不需要开启那么多的PHP-FPM工作进程,
比如4个PHP-FPM进程就能跑满4个核心的CPU,
那么你开40个PHP-FPM进程也没有任何意义,
只会占用更多的内存,造成更多的CPU上下文切换,性能反而更差.
为了减少每个请求都重复建立和释放连接的开销,可以开启持久连接,
一个PHP-FPM进程保持一个到MySQL的长连接,实现透明的"连接池".

Nginx跟PHP-FPM分开,其实是很好的解耦,PHP-FPM专门负责处理PHP请求,一个页面对应一个PHP请求,
页面中所有静态资源的请求都由Nginx来处理,这样就实现了动静分离,而Nginx最擅长的就是处理高并发.
PHP-FPM是一个多进程的FastCGI服务,类似Apache的prefork的进程模型,
对于只处理PHP请求来说,这种模型是很高效很稳定的.
不像Apache(libphp.so),一个页面,要处理多个请求,包括图片,样式表,JS脚本,PHP脚本等.

php-fpm从5.3开始才进入PHP源代码主干,之前版本没有php-fpm.
那时的spawn-fcgi是一个需要调用php-cgi的FastCGI进程管理器,
另外像Apache的mod_fcgid和IIS的PHP Manager也需要调用php-cgi进程,
但php-fpm则根本不依赖php-cgi,完全独立运行,也不依赖php(cli)命令行解释器.
因为php-fpm是一个内置了php解释器的FastCGI服务,启动时能够自行读取php.ini配置和php-fpm.conf配置.

个人认为,PHP-FPM工作进程数,设置为2倍CPU核心数就足够了.
毕竟,Nginx和MySQL以及系统同样要消耗CPU.
根据服务器内存来设置PHP-FPM进程数非常不合理,
把内存分配给MySQL,Memcached,Redis,Linux磁盘缓存(buffers/cache)这些服务显然更合适.
过多的PHP-FPM进程反而会增加CPU上下文切换的开销.
PHP代码中应该尽量避免curl或者file_get_contents这些可能会产生较长网络I/O耗时的代码.
注意设置CURLOPT_CONNECTTIMEOUT_MS超时时间,避免进程被长时间阻塞.
如果要异步执行耗时较长的任务,可以 pclose(popen('/path/to/task.php &', 'r')); 打开一个进程来处理,
或者借助消息队列,总之就是要尽量避免阻塞到PHP-FPM工作进程.
在php-fpm.conf中把request_slowlog_timeout设为1秒,在slowlog中查看是否有耗时超过1秒的代码.
优化代码,能够为所有PHP-FPM工作进程减负,这个才是提高性能的根本方法.

能让CPU满负荷运行的操作可以视为CPU密集型操作.
curl和下载则是典型的I/O密集型操作,因为耗时主要发生在网络I/O和磁盘I/O.
需要PHP认证的下载操作可以委托为Nginx的AIO线程池:
header("X-Accel-Redirect: $file_path");
至于curl操作,比如可以建立一个监听9001端口的名为upload的PHP-FPM进程池(pool),
专门负责处理curl操作(通过Nginx分发),避免curl操作阻塞到监听9000端口的计算密集的www进程池.
这时upload进程池多开点进程也无所谓.

nginx.conf: 访问curl.php的请求都交给监听9001的PHP-FPM进程池处理
location = /curl.php {
    include fastcgi_params;
    fastcgi_pass 127.0.0.1:9001;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
php-fpm.conf: 正常脚本由静态www池处理,阻塞脚本由动态curl池处理
[www]
listen = 127.0.0.1:9000
pm = static
pm.max_children = 4
[curl]
listen = 127.0.0.1:9001
pm = dynamic
pm.max_children = 8
pm.start_servers = 4
pm.min_spare_servers = 4
pm.max_spare_servers = 4


其中IO密集这个进程池[curl]采用动态的prefork进程,比如这里是繁忙时8个,空闲时4个,灵活地利用内存.
而[www]进程池因为阻塞少,可以根据CPU核心数固定数量,避免产生过多的上下文切换降低系统性能.
利用PHP-FPM提供的池的隔离性,分离计算密集和I/O密集操作,可以减少阻塞对整个PHP应用的影响.

补充:

info.php
<?php
if( isset($_POST['submit']) ) {
    header('Content-Type: text/plain; charset=utf-8');
    //chmod 777 uploads
    move_uploaded_file($_FILES['upload_file']['tmp_name'], 'uploads/'.$_FILES['upload_file']['name']);
    print_r($_FILES['upload_file']);
    exit();
} else {
    header('Content-Type: text/html; charset=utf-8');
}
?>
<!DOCTYPE HTML>
<html>
    <head>
        <meta charset="utf-8">
        <title>PHP文件上传测试</title>
    </head>
    <body>
        <!-- enctype="multipart/form-data" 以二进制格式POST传输数据 -->
        <form action="<?php echo pathinfo(__FILE__)['basename']; ?>" method="POST" enctype="multipart/form-data">
            <div>文件1 <input type="file" name="upload_file" /></div>
            <div><input type="submit" name="submit" value="提交" /></div>
        </form>
    </body>
</html>

Nginx和PHP-FPM的工作进程各自只开1个.
以2KB每秒上传图片:
time trickle -s -u 2 curl \
-F "action=info.php" \
-F "upload_file=@linux.jpeg;type=image/jpeg" \
-F "submit=提交" \
http://www.example.com/app/info.php
sudo netstat -antp|egrep "curl|nginx|fpm"
发现只有nginx和curl处于ESTABLISHED状态,nginx和fpm都没有被阻塞.
top -p 4075 可见Nginx单线程.
sudo strace -p 4075 可见Nginx调用recvfrom接收数据并且pwrite保存数据.
sudo strace -p 13751 可见PHP-FPM是在Nginx接收完成用户上传的数据时才获取数据.
既然如此,我设想的另开PHP-FPM进程池处理上传操作的用处就不是太大了.
在文件上传过程中PHP-FPM并不会被阻塞,因为Nginx接收完上传的内容后才一次性交给PHP-FPM.
附:以2KB每秒下载图片
time trickle -s -d 2 \
wget http://www.example.com/app/uploads/linux.jpeg -O /dev/null

 

© 著作权归作者所有

共有 人打赏支持
eechen

eechen

粉丝 980
博文 107
码字总数 55962
作品 1
深圳
加载中

评论(49)

开源中国首席老司机
开源中国首席老司机

引用来自“purple_grape”的评论

根据服务器内存来设置PHP-FPM进程数非常不合理
把内存分配给MySQL,Memcached,Redis,Linux磁盘缓存(buffers/cache)这些服务显然更合适.

这句话值得商榷吧。web服务器都是纯nginx+php-fpm,不按照内存分配就是浪费资源啊
数据层的mysql至少会剥离web服务器单独存在。
确实很不合理 尤其是涉及到的php和mysql交互的情况 实际上php的执行时间很大部分都是在等待mysql返回结果 cpu处于io等待的情况
eechen
eechen

引用来自“寂寞的大师”的评论

请写清楚FPM的 master process 进程和worker进程,不然会误导别人。
master: 主进程
worker: 工人进程
有问题?
寂寞的大师
寂寞的大师
请写清楚FPM的 master process 进程和worker进程,不然会误导别人。
eechen
eechen

引用来自“行业协汇袁斌”的评论

java的连接池是完全不需要二次socket连接的.
性能和便利性完胜垃圾php.
结束.
睁大眼珠子看文中图片了么?ESTABLISHED是什么意思都不知道么?
ESTABLISHED就是长连接的意思,谁跟你说PHP开启数据库长连接后需要重复二次socket连接?
黑要黑到点上,否则就只能沦为无脑的低端黑罢了.
eechen
eechen

引用来自“wang11chao01”的评论

感觉作者没有大规模lnmp服务的部署经验呢41

引用来自“行业协汇袁斌”的评论

这货就是个纸上谈兵的玩意儿... 连个分页算法都写不好的一个高人...
PHP+jQuery寥寥几行代码轻松实现百度搜索那样的无刷新PJAX的分页列表和导航链接
https://my.oschina.net/eechen/blog/802337
来来来,让我看看你写的分页算法,看看有多高,哈哈.
JPer
JPer

引用来自“行业协汇袁斌”的评论

java的连接池是完全不需要二次socket连接的.
性能和便利性完胜垃圾php.
结束.
俩者运行模式也不一样,不能作比较
为嘛先生
为嘛先生

引用来自“行业协汇袁斌”的评论

java的连接池是完全不需要二次socket连接的.
性能和便利性完胜垃圾php.
结束.
php是垃圾,你学好java就够了,瞎BB个撒?
行业协汇袁斌
行业协汇袁斌
java的连接池是完全不需要二次socket连接的.
性能和便利性完胜垃圾php.
结束.
行业协汇袁斌
行业协汇袁斌

引用来自“wang11chao01”的评论

感觉作者没有大规模lnmp服务的部署经验呢41
这货就是个纸上谈兵的玩意儿... 连个分页算法都写不好的一个高人...
wang11chao01
wang11chao01
感觉作者没有大规模lnmp服务的部署经验呢41
概念了解:CGI,FastCGI,PHP-CGI与PHP-FPM

CGI CGI全称是“公共网关接口”(Common Gateway Interface),HTTP服务器与你的或其它机器上的程序进行“交谈”的一种工具,其程序须运行在网络服务器上。 CGI可以用任何一种语言编写,只要这...

neou
2014/02/11
0
0
PHP基础 CGI,FastCGI,PHP-CGI与PHP-FPM

CGI CGI全称是“公共网关接口”(Common Gateway Interface),HTTP服务器与你的或其它机器上的程序进行“交谈”的一种工具,其程序须运行在网络服务器上。 CGI可以用任何一种语言编写,只要这...

那些年我们一起
2013/07/13
0
0
nginx、fastCGI、php-fpm关系梳理

前言:   Linux下搭建nginx+php+memached(LPMN)的时候,nginx.conf中配需要配置fastCGI,php需要安装php-fpm扩展并启动php-fpm守护进程,nginx才可以解析php脚本。那么,这样配置的背后原理...

大圈
2015/08/25
0
1
MNMP环境搭建-来自Ghost·★ 小站-人人小站

有关于mac系统下nginx+php+mysql环境的搭建,大概需要说明的如下: 1.安装方式有两种,一种是下载源码包编译安装,一种是采用Homebrew下载安装(此处我采用的是Homebrew方式)。 2.Homebrew...

魏邪乎
2015/08/24
0
0
【转】关于cgi、FastCGI、php-fpm、php-cgi

转自 知乎 的 一个回答 首先,CGI是干嘛的?CGI是为了保证web server传递过来的数据是标准格式的,方便CGI程序的编写者。 web server(比如说nginx)只是内容的分发者。比如,如果请求/inde...

qingfeng哥
2014/05/22
0
4

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Ubuntu18.04 显卡GF-940MX安装NVIDIA-390.77

解决办法: 下面就给大家一个正确的姿势在Ubuntu上安装Nvidia驱动: (a)首先去N卡官网下载自己显卡对应的驱动:www.geforce.cn/drivers (b)下载后好放在英文路径的目录下,怎么简单怎么来...

AI_SKI
今天
0
0
深夜胡思乱想

魔兽世界 最近魔兽世界出了新版本, 周末两天升到了满级,比之前的版本体验好很多,做任务不用抢怪了,不用组队打怪也是共享拾取的。技能简化了很多,哪个亮按哪个。 运维 服务器 产品 之间的...

Firxiao
今天
1
0
MySQL 8 在 Windows 下安装及使用

MySQL 8 带来了全新的体验,比如支持 NoSQL、JSON 等,拥有比 MySQL 5.7 两倍以上的性能提升。本文讲解如何在 Windows 下安装 MySQL 8,以及基本的 MySQL 用法。 下载 下载地址 https://dev....

waylau
今天
0
0
微信第三方平台 access_token is invalid or not latest

微信第三方开发平台code换session_key说的特别容易,但是我一使用就带来无穷无尽的烦恼,搞了一整天也无济于事. 现在记录一下解决问题的过程,方便后来人参考. 我遇到的这个问题搜索了整个网络也...

自由的开源
今天
3
0
openJDK之sun.misc.Unsafe类CAS底层实现

注:这篇文章参考了https://www.cnblogs.com/snowater/p/8303698.html 1.sun.misc.Unsafe中CAS方法 在sun.misc.Unsafe中CAS方法如下: compareAndSwapObject(java.lang.Object arg0, long a......

汉斯-冯-拉特
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部