🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## php\-fpm是什么?  PHP5.3.3开始集成了php-fpm模块,不再是第三方的包了。PHP\-FPM提供了更好的PHP[进程管理](https://baike.baidu.com/item/%E8%BF%9B%E7%A8%8B%E7%AE%A1%E7%90%86)方式,可以有效控制内存和进程、可以平滑\[重载\](https://baike.baidu.com/item/%E9%87%8D%E8%BD%BD)PHP配置。  重点:  > php-fpm是fastcgi的实现。  ## php-fpm的运行模型?  > 多进程同步阻塞模式  php-fpm是一种master(主)/worker(子)多进程架构模型。  当PHP-FPM启动时,会读取配置文件,然后创建一个Master进程和若干个Worker进程(具体是几个Worker进程是由php-fpm.conf中配置的个数决定)。Worker进程是由Master进程fork出来的。  **master进程主要负责CGI及PHP环境初始化、事件监听、Worker进程状态等等,worker进程负责处理php请求**。  master进程负责创建和管理woker进程,同时负责监听listen连接,**master进程是多路复用的**;woker进程负责accept请求连接,同时处理请求,**一个woker进程可以处理多个请求**(**复用**,不需要每次都创建销毁woker进程,而是达到处理一定请求数后销毁重新fork创建worker进程),**但一个woker进程一次只能处理一个请求**。  https://img.kancloud.cn/41/ed/41ed99b258dd1703acd803c8de18db1f_640x226.png ## cgi,php-cgi,php-fpm,fastcgi的区别?  **cgi**  cgi是一个web server与cgi程序(这里可以理解为是php解释器)之间进行数据传输的协议,保证了传递的是标准数据。(服务端和解释器之间的数据传输协议)  ***php-cgi**  php-cgi是php解释器。他自己本身只能解析请求,返回结果,不会管理进程。php-fpm是调度管理php-cgi进程的程序。(php解释器,由php-fpm调度)  ***Fastcgi** Fastcgi是用来提高cgi程序(php\-cgi)性能的方案/协议。  cgi程序的性能问题在哪呢?"PHP解析器会解析php.ini文件,初始化执行环境",就是这里了。**标准的CGI对每个请求都会执行这些步骤,所以处理的时间会比较长**。  Fastcgi会先启一个master,解析配置文件,初始化执行环境,然后再启动多个worker。当请求过来时,master会传递给一个worker,然后立即可以接受下一个请求。这样就避免了重复劳动,效率自然提高。而且当worker不够用时,master可以根据配置预先启动几个worker等着;当然空闲worker太多时,也会停掉一些,这样就提高了性能,也节约了资源。这就是Fastcgi的对进程的管理。  \* \*\*php-fpm\*\*  \*\*fastcgi是一个方案或者协议,php-fpm就是FastCGI的后端实现\*\*,也就是说,进程分配和管理是FPM来做的。官方对FPM的解释:【Fastcgi Process Manager】【Fastcgi 进程管理器】。  php\-fpm的管理对象是php\-cgi,他负责管理一个进程池,来处理来自Web服务器的请求。  对于php.ini文件的修改,php\-cgi进程是没办法平滑重启的,有了php\-fpm后,就把平滑重启成为了一种可能,php\-fpm对此的处理机制是新的worker用新的配置,已经存在的worker处理完手上的活就可以歇着了,通过这种机制来平滑过度的。  ## php\-fpm如何完成平滑重启?  修改php.ini之后,php\-cgi进程的确是没办法平滑重启的。php\-fpm对此的处理机制是新的worker用新的配置,已经存在的worker处理完手上的活就可以歇着了,通过这种机制来平滑过度。  ## php\-fpm和nginx的通信机制是怎么样的?  看下nginx的配置文件: Nginx中fastcgi_pass的配置:  ~~~ location ~ .php$ { root /home/wwwroot; fastcgi_pass 127.0.0.1:9000; #fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; #fastcgi_pass unix:/tmp/php-cgi.sock; try_files $uri /index.php =404; fastcgi_split_path_info ^(.+\\.php)(/.+)$; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }   ~~~  因为nginx不能直接执行php,所以需要借用fastcgi模块,和php\-fpm进行通信。有两种方式;  **1. TCP** **2. UNIX Domain Socket**  TCP是IP加端口,可以跨服务器;  而UNIX Domain Socket不经过网络,只能用于Nginx跟PHP\-FPM都在同一服务器的场景。具体配置:  ~~~ 方式1: php-fpm.conf: listen = 127.0.0.1:9000 nginx.conf: fastcgi_pass 127.0.0.1:9000; 方式2: php-fpm.conf: listen = /tmp/php-fpm.sock nginx.conf: fastcgi_pass unix:/tmp/php-fpm.sock;  ~~~  值得一提的是,MySQ命令行客户端连接mysqld服务也类似有这两种方式: 使用Unix Socket连接(默认):  ~~~ mysql -uroot -p --protocol=socket --socket=/tmp/mysql.sock   ~~~  使用TCP连接:  ~~~ mysql -uroot -p --protocol=tcp --host=127.0.0.1 --port=3306 ~~~ ## php-fpm在请求链路的体现,画出来?  ~~~ www.example.com | | \\!/ Nginx | | \\!/ 路由到www.example.com/index.php | | \\!/ 加载nginx的fast-cgi模块 | | \\!/ fast-cgi监听127.0.0.1:9000地址 | | \\!/ www.example.com/index.php请求到达127.0.0.1:9000 | | \\!/ php-fpm 监听127.0.0.1:9000 | | \\!/ php-fpm 接收到请求,启用worker进程处理请求 | | \\!/ php-fpm 处理完请求,返回给nginx | | \\!/ nginx将结果通过http返回给浏览器  ~~~  ## php-fpm有几种工作模式?  PHP-FPM进程管理方式有动态(Dynamic)、静态(Static)、按需分配(Ondemand)三种。  **动态**  会初始化创建一部分worker,在运行过程中,动态调整worker数量,最大worker数受pm.max_children和process.max  ~~~ listen = 127.0.0.1:9001 pm = dynamic pm.max_children = 10 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 6   ~~~  1. 当空闲进程数小于min_spare_servers时,创建新的子进程,总子进程数小于等于pm.max_children,小于等于process.max 2. 当空闲进程数大于max_spare_servers,会杀死启动时间最长的子进程 3. 如果子进程(idle状态)数大于max_children,会打印warning日志,结束处理 4. process小于 max_children ,计算一个num,启动num个worker 5. 优点:动态扩容,不浪费系统资源 6. 缺点:所有worker都在工作,新的请求到来需要等待创建worker进程,最长等待1s(内部存在一个1s的定时器,去查看,创建进程),频繁启停进程消耗cpu,请求数稳定,不需要频繁销毁  **静态**  启动固定大小数量的worker,也有1s的定时器,用于统计进程的一些状态信息,例如空闲worker个数,活动worker个数  ~~~ pm.max_children = 10 #必须配置这个参数,而且只有这个参数有效   ~~~  1. 优点:不用动态判断负载,提升性能 2. 缺点:如果配置成static,只需要考虑max_children数量,数量取决于cpu的个数和应用的响应时间,一次启动固定大小进程浪费系统资源  **按需分配**  php-fpm启动的时候不会启动worker进程,按需启动worker,有链接进来后,才会启动  ~~~ listen = 127.0.0.1:9001 pm = ondemand pm.process_idle_timeout = 60 pm.max_children = 10   ~~~  连接到来时(只有链接,不没有数据也会创建,telnet也会创建),创建新worker进程,worker进程数的创建收max_children设置限制,也受限于全局的process.max设置(三种模式都受限此,下文中有全局配置项讲解),如果空闲时间超过了process_idle_timeout的设置就会销毁worker进程  * 优点:按流量需求创建,不浪费系统资源, * 缺点:因为php-fpm是短连接的,如果每次请求都先建立连接,大流量场景下会使得master进程变得繁忙,浪费cpu,不适合大流量模式 * 不推荐使用此模式  | 工作模式 | 特点 | | --- | --- | | 动态 | 均衡优先,适合小内存服务器,2g左右 | | 静态 | 性能优先, 适合大内存机器 | | 按需分配 | 内存优先,适合微小的内存,2g以下 |  ## 怎么选定php-fpm的worker进程数?  * 动态建立进程个数 * N+20% 到 M/m之间 * N是cpu核数,M是内存,m是每个php进程内存数 * 静态进程个数 * M/(m*1.2) * pm.max_requests, 设置最大请求数,达到这个数量以后,会自动长期worker进程,繁殖内存意外增长  注意:PHP程序在执行完成后,或多或少会有内存泄露的问题。这也是为什么开始的时候一个php-fpm进程只占用3M左右内存,运行一段时间后就会上升到20-30M。所以需要每个worker进程处理完一定的请求后,销毁重新创建。  cpu密集型的pm.max_children不能超过cpu内核数,但是web服务属于IO密集型的,可以将pm.max_children的值设置大于cpu核数。  ## php-fpm如何优化?  (1)避免程序跑死(hang)  在负载较高的服务器上定时重载php-fpm,reload可以平滑重启而不影响生产系统的php脚本运行,每15分钟reload一次,定时任务如下:  ~~~ 0-59/15 * * * * /usr/local/php/sbin/php-fpm reload   ~~~  (2)合理增加单个worker进程最大处理请求数,减少内存消耗  最大处理请求数是指一个php-fpm的worker进程在处理多少个请求后就终止掉,master进程会重新respawn新的。该配置可以避免php解释器自身或程序引起的memory leaks。默认值是500,可以修改为如下配置:  ~~~ pm.max_requests = 1024  ~~~  (3)开启静态模式,指定数量的php-fpm进程,减少内存消耗