PHP-FPM进程池探秘
2018-06-22 05:29:28来源:未知 阅读 ()
PHP 7.2以前的版本只支持多进程而不支持多线程;PHP 7.2+ pthreads 扩展提供了Thread、Worker、Threaded 对象,使得创建、读取、写入以及执行多线程成为可能,并可以在多个线程之间进行同步控制;pthreads 多线程开发也仅限于命令行模式,不能用于 web 服务器环境中。
PHP-FPM 在进程池中运行多个子进程并发处理所有连接请求。通过 ps 查看PHP-FPM进程池(pm.start_servers = 2)状态如下:
root@d856fd02d2fe:~# ps aux -L USER PID LWP %CPU NLWP %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 1 0.0 1 0.0 4504 692 ? Ss 13:10 0:00 /bin/sh /usr/local/php/bin/php-fpm start root 7 7 0.0 1 0.4 176076 19304 ? Ss 13:10 0:00 php-fpm: master process (/usr/local/php/etc/php-fpm.conf) www-data 8 8 0.0 1 0.2 176076 8132 ? S 13:10 0:00 php-fpm: pool www www-data 9 9 0.0 1 0.2 176076 8132 ? S 13:10 0:00 php-fpm: pool www root 10 10 0.0 1 0.0 18376 3476 ? Ss 14:11 0:00 bash root 66 66 0.0 1 0.0 34420 2920 ? R+ 15:13 0:00 ps aux -L
从列表中可以看出,进程池www中有两个尚处于空闲状态的子进程PID 8和 PID 9。注:NLWP指轻量级进程数量,即线程数量。
为什么需要PHP-FPM(FastCGI Process Manager)?
FastCGI is a kind of CGI which is long-live, which will always be running.
PHP-CGI is one kind of the Process Manager of FastCGI, which is within php itself.After changing php.ini, you should reboot PHP-CGI to make the new php.ini work.When a PHP-CGI process is killed, all the PHP code will cannot run.
PHP-FPM is another kind of the Process Manager of FastCGI.PHP-FPM can be used to control sub processes of PHP-CGI.
- FastCGI是语言无关的、可伸缩架构的CGI开放扩展,其主要行为是将CGI解释器进程一直保持在内存,不是fork-and-execute,并因此获得较高的性能。FastCGI支持分布式部署,可以部署在WEB服务器以外的多个主机上。
- PHP-CGI作为PHP自带的PHP FastCGI管理器对FastCGI的管理方式简单,也不够灵活高效。
- PHP-FPM为了解决PHP-CGI的不足,为PHP FastCGI提供了一种新的进程管理方式,可以有效控制进程,平滑重载PHP配置,其master process是常驻内存的,worker process有static、dynamic、ondemand三种管理方式。PHP-FPM进程池中的CGI在接受并处理完pm.max_requests个用户请求后将会respawn,并不会保证单个CGI是long-live and always be running,而会以更加灵活高效的方式来保证客户端的连接请求可以被多个CGI处理。
static - a fixed number (pm.max_children) of child processes; dynamic - the number of child processes are set dynamically based on the following directives. With this process management, there will be always at least 1 children. pm.max_children - the maximum number of children that can be alive at the same time. pm.start_servers - the number of children created on startup. pm.min_spare_servers - the minimum number of children in 'idle' state (waiting to process). If the number of 'idle' processes is less than this number then some children will be created. pm.max_spare_servers - the maximum number of children in 'idle' state (waiting to process). If the number of 'idle' processes is greater than this number then some children will be killed. ondemand - no children are created at startup. Children will be forked when new requests will connect. The following parameter are used: pm.max_children - the maximum number of children that can be alive at the same time. pm.process_idle_timeout - The number of seconds after which an idle process will be killed.
PHP-FPM探秘手段:模拟多线程并发执行
1. 什么是线程:参考本人此篇 为什么要使用线程 。
2. 模拟多线程:
1 <?php 2 /** 3 * PHP 只支持多进程不支持多线程。 4 * 5 * PHP-FPM 在进程池中运行多个子进程并发处理所有请求, 6 * 同一个子进程可先后处理多个请求,但同一时间 7 * 只能处理一个请求,未处理请求将进入队列等待处理 8 * 9 */ 10 11 class SimulatedThread 12 { 13 //模拟线程标识 14 private $threadID; 15 16 //主机名 17 private $host = 'tcp://172.17.0.5'; 18 19 //端口号 20 private $port = 80; 21 22 public function __construct() 23 { 24 //采用当前时间给线程编号 25 $this->threadID = microtime(true); 26 } 27 28 /** 29 * 通过socket发送一个新的HTTP连接请求到本机, 30 * 此时当前模拟线程既是服务端又是模拟客户端 31 * 32 * 当前(程序)子进程sleep(1)后会延迟1s才继续执行,但其持有的请求是继续有效的, 33 * 不能处理新的请求,故这种做法会降低进程池处理并发多个请求的能力, 34 * 类似延迟处理还有time_nanosleep()、time_sleep_until()、usleep()。 35 * 而且sleep(1)这种做法并不安全,nginx依然可能出现如下错误: 36 * “epoll_wait() reported that client prematurely closed connection, 37 * so upstream connection is closed too while connecting to upstream” 38 * 39 * @return void 40 */ 41 public function simulate() 42 { 43 $run = $_GET['run'] ?? 0; 44 if ($run++ < 9) {//最多模拟10个线程 45 $fp = fsockopen($this->host, $this->port); 46 fputs($fp, "GET {$_SERVER['PHP_SELF']}?run={$run}\r\n\r\n"); 47 sleep(1);//usleep(500) 将延迟 500 微妙(us),1 s = 1000000 us 48 fclose($fp); 49 } 50 51 $this->log(); 52 } 53 54 /** 55 * 日志记录当前模拟线程运行时间 56 * 57 * @return void 58 */ 59 private function log() 60 { 61 $fp = fopen('simulated.thread', 'a'); 62 fputs($fp, "Log thread {$this->threadID} at " . microtime(true) . "(s)\r\n"); 63 64 fclose($fp); 65 } 66 } 67 68 $thread = new SimulatedThread(); 69 $thread->simulate(); 70 echo "Started to simulate threads...";
PHP-FPM探秘汇总:本人通过运行上述脚本后,发现一些可预料但却不是我曾想到的结果
1. PHP-FPM配置项pm.max_children = 5,执行sleep(1)延迟,模拟线程数10,simulated.thread记录如下:
Log thread 1508054181.4236 at 1508054182.4244(s)
Log thread 1508054181.4248 at 1508054182.4254(s)
Log thread 1508054181.426 at 1508054182.428(s)
Log thread 1508054181.6095 at 1508054182.6104(s)
Log thread 1508054182.4254 at 1508054183.4262(s)
Log thread 1508054183.4272 at 1508054183.4272(s)
Log thread 1508054182.4269 at 1508054183.4275(s)
Log thread 1508054182.4289 at 1508054183.43(s)
Log thread 1508054182.6085 at 1508054183.6091(s)
Log thread 1508054182.611 at 1508054183.6118(s)
最新生成的(模拟)线程登记出现在红色标示条目位置是因为进程池的并发连接处理能力上限为5,因此它只可能出现在第六条以后的位置。记录的时间跨度 1508054183.6118 - 1508054181.4236 = 2.1882(s)。下面是同等条件下的另一次测试结果:
Log thread 1508058075.042 at 1508058076.0428(s) Log thread 1508058075.0432 at 1508058076.0439(s) Log thread 1508058075.0443 at 1508058076.045(s) Log thread 1508058075.6623 at 1508058076.6634(s) Log thread 1508058076.0447 at 1508058077.0455(s) Log thread 1508058076.046 at 1508058077.0466(s) Log thread 1508058077.0465 at 1508058077.0466(s) Log thread 1508058076.0469 at 1508058077.0474(s) Log thread 1508058076.6647 at 1508058077.6659(s) Log thread 1508058076.6664 at 1508058077.6671(s)
有意思的是绿色条目代表的(模拟)线程和红色条目代表的(模拟)线程的登记时间是一样的,说明两个(模拟)线程是并发执行的。记录的时间跨度 1508058077.6671 - 1508058075.042 = 2.6251(s)。模拟线程数改为51后,simulated.thread记录如下:
Log thread 1508304245.2524 at 1508304246.3104(s)
Log thread 1508304245.3112 at 1508304246.3119(s)
Log thread 1508304245.461 at 1508304246.4619(s)
Log thread 1508304246.3131 at 1508304247.3141(s)
Log thread 1508304246.3432 at 1508304247.3439(s)
...
Log thread 1508304254.4762 at 1508304255.4767(s)
Log thread 1508304255.4768 at 1508304255.4768(s)
Log thread 1508304255.3284 at 1508304256.3292(s)
Log thread 1508304255.3584 at 1508304256.3593(s)
Log thread 1508304255.4757 at 1508304256.4763(s)
红色条目代表的(模拟)线程创建时间最晚。记录的时间跨度 1508304256.4763 - 1508304245.2524 = 11.2239(s)。
2. PHP-FPM配置项pm.max_children = 10,执行sleep(1)延迟,模拟线程数10,simulated.thread记录如下:
Log thread 1508061169.7956 at 1508061170.7963(s) Log thread 1508061169.7966 at 1508061170.7976(s) Log thread 1508061169.7978 at 1508061170.7988(s) Log thread 1508061170.2896 at 1508061171.2901(s) Log thread 1508061170.7972 at 1508061171.7978(s) Log thread 1508061171.7984 at 1508061171.7985(s) Log thread 1508061170.7982 at 1508061171.7986(s) Log thread 1508061170.7994 at 1508061171.8(s) Log thread 1508061171.2907 at 1508061172.2912(s) Log thread 1508061171.2912 at 1508061172.2915(s)
由于服务端并发连接处理能力上限达到10,因此最新生成的(模拟)线程登记可出现在任何位置。记录的时间跨度 1508061172.2915 - 1508061169.7956 = 2.4959(s)。模拟线程数改为51后,simulated.thread记录如下:
Log thread 1508307376.5733 at 1508307377.5741(s)
Log thread 1508307376.5748 at 1508307377.5759(s)
...
Log thread 1508307382.5883 at 1508307383.589(s)
Log thread 1508307383.5898 at 1508307383.5899(s)
Log thread 1508307382.5896 at 1508307383.5904(s)
Log thread 1508307382.708 at 1508307383.7088(s)
Log thread 1508307382.7091 at 1508307383.7095(s)
...
Log thread 1508307382.716 at 1508307383.7166(s)
Log thread 1508307382.7172 at 1508307383.7178(s)
Log thread 1508307383.5883 at 1508307384.5891(s)
红色条目代表的(模拟)线程创建时间最晚。记录的时间跨度 1508307384.5891 - 1508307376.5733 = 8.0158(s)。
3. PHP-FPM配置项pm.max_children = 5,执行usleep(500)延迟,模拟线程数10,simulated.thread记录如下:
Log thread 1508059270.3195 at 1508059270.3206(s)
Log thread 1508059270.3208 at 1508059270.3219(s)
Log thread 1508059270.322 at 1508059270.323(s)
Log thread 1508059270.323 at 1508059270.324(s)
Log thread 1508059270.3244 at 1508059270.3261(s)
Log thread 1508059270.3256 at 1508059270.3271(s)
Log thread 1508059270.3275 at 1508059270.3286(s)
Log thread 1508059270.3288 at 1508059270.3299(s)
Log thread 1508059270.3299 at 1508059270.331(s)
Log thread 1508059270.3313 at 1508059270.3314(s)
可见日志记录顺序与(模拟)线程生成的顺序一致,但除红色标示条目外,其他条目看不出是并发执行的,更像是一个接一个串行顺序执行完的。记录的时间跨度 1508059270.3314 - 1508059270.3195 = 0.0119(s)。
从以上的记录可以看出:
1)这些(模拟)线程是第一次请求执行脚本后就自动生成的,一个(模拟)线程触发另一个(模拟)线程的创建;
2)这些(模拟)线程中有的虽是在同一个子进程空间中产生并运行的,但有先后顺序,即前一个执行完退出后下一个才能创建并运行,再加上这些模拟线程实际上都是以多进程运行的,所以它们并发执行的效率比真正多线程并发执行效率要低。对此要提高并发处理能力的有效途径是增加子进程数量,避免顺序执行,减少连接请求进入队列长时间等待的概率;
3)前后相邻(模拟)线程生成时间间隔很小,几乎是同时产生,或后一个(模拟)线程在前一个(模拟)线程尚未执行结束并退出之前产生,这是并发执行的条件;
4)多个(模拟)线程可以并发执行,也就是说它们模拟了对同一目标任务(这里就是运行日志登记,当然也可以是其他目标任务)的多线程并发处理。只是这里多个(模拟)线程之间是完全独立的,没有共享当前进程资源,但是都拥有对磁盘文件simulated.thread的写操作。
上述第4条说明模拟多线程的基本目标已实现,所以模拟多线程并发的实现是成功的,其最大好处就是可以充分利用进程池并发处理连接请求的能力。PHP-FPM进程池中同一个子进程可先后处理多个请求,但同一时间只能处理一个请求,未处理请求将进入队列等待处理。换句话,同一个子进程不具有并发处理多个请求的能力。
PHP-FPM Pool配置:它允许定义多个池,每个池可定义不同的配置项。以下只是列举了我在探秘过程中还关注过的其他部分配置项
1. listen:The address on which to accept FastCGI requests.它支持TCP Socket和unix socket两种通讯协议。可设置listen = [::]:9000。
2. listen.allowed_clients:List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. 该配置项为逗号分隔的列表,如listen.allowed_clients = 127.0.0.1,172.17.0.5。
3. pm:Choose how the process manager will control the number of child processes. 该配置项设置FPM管理进程池的方式,包括static、dynamic、ondemand三种。
4. pm.max_requests:The number of requests each child process should execute before respawning. This can be useful to work around memory leaks in 3rd party libraries.设置每个子进程处理请求数的上限,对于处理第三方库中的内存泄漏很有用。
5. pm.status_path:The URI to view the FPM status page.
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- PHP多进程之pcntl_fork的实例详解 2020-03-22
- 基于PHP-FPM进程池探秘 2020-03-22
- PHP多进程编程之僵尸进程问题的理解 2020-02-17
- 详解php-fpm配置的操作方法 2019-10-08
- 全面讲解PHP中多进程编程的相关函数 2019-09-17
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash