Home >Backend Development >PHP Tutorial >Exploration based on PHP-FPM process pool
PHP supports multi-process but not multi-threading; PHP-FPM runs multiple child processes in the process pool to handle all connection requests concurrently. Check the status of the PHP-FPM process pool (pm.start_servers = 2) through ps as follows:
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
As can be seen from the list, there are two idle child processes PID 8 and PID in the process pool www 9. Note: NLWP refers to the number of lightweight processes, that is, the number of threads.
What is PHP-FPM (FastCGI Process Manager)? PHP-FPM provides a process management method for PHP-CGI, which can effectively control memory and processes, and can smoothly reload PHP configuration. Its master process is resident in memory. FastCGI is a language-independent, scalable architecture CGI open extension. Its main behavior is to keep the CGI interpreter process in memory for a longer time, rather than fork-and-execute, and therefore obtain higher performance. FastCGI supports distributed deployment and can be deployed on multiple hosts other than the WEB server.
Methods to explore: simulate multi-threaded concurrent execution
Related learning recommendations:PHP programming from entry to proficiency
1. What is a thread: A thread is sometimes called a lightweight process (LWP). It usually consists of a thread ID, the current instruction pointer (PC), a register set and a stack. An entity in the process is the basic unit that is independently scheduled by the system; the thread itself does not own system resources, only some resources that are essential for operation, and shares all resources owned by the process with other threads belonging to the same process. Due to the mutual constraints between threads, threads show discontinuity in their operation. Threads also have three basic states: ready, blocked and running. Since the process is the resource owner, the creation, cancellation and switching overhead are too high, so running multiple threads (Threads) simultaneously on a symmetric multiprocessor (SMP) is a more appropriate choice. The entity of the thread includes program, data and thread control block (TCB). TCB includes the following information:
(1) Thread status;
(2) When the thread When not running, saved on-site resources;
(3) A set of execution stacks;
(4) Main memory that stores local variables of each thread;
(5) Access main memory and other resources in the same process.
But using multiple processes will make the application more robust in the event that a process in the process pool crashes or is attacked.
2. Simulate multi-threading:
<?php /** * PHP 只支持多进程不支持多线程。 * * PHP-FPM 在进程池中运行多个子进程并发处理所有连接, * 同一个子进程可先后处理多个连接请求,但同一时间 * 只能处理一个连接请求,未处理连接请求将进入队列等待处理 * */ class SimulatedThread { //模拟线程 private $thread; //主机名 private $host = 'tcp://172.17.0.5'; //端口号 private $port = 80; public function __construct() { //采用当前时间给线程编号 $this->thread = microtime(true); } /** * 通过socket发送一个新的HTTP连接请求到本机, * 此时当前模拟线程既是服务端又是模拟客户端 * * 当前(程序)子进程sleep(1)后会延迟1s才继续执行,但其持有的连接是继续有效的, * 不能处理新的连接请求,故这种做法会降低进程池处理并发连接请求的能力, * 类似延迟处理还有time_nanosleep()、time_sleep_until()、usleep()。 * 而且sleep(1)这种做法并不安全,nginx依然可能出现如下错误: * “epoll_wait() reported that client prematurely closed connection, * so upstream connection is closed too while connecting to upstream” * * @return void */ public function simulate() { $run = $_GET['run'] ?? 0; if ($run++ < 9) {//最多模拟10个线程 $fp = fsockopen($this->host, $this->port); fputs($fp, "GET {$_SERVER['PHP_SELF']}?run={$run}\r\n\r\n"); sleep(1);//usleep(500) fclose($fp); } $this->log(); } /** * 日志记录当前模拟线程运行时间 * * @return void */ private function log() { $fp = fopen('simulated.thread', 'a'); fputs($fp, "Log thread {$this->thread} at " . microtime(true) . "(s)\r\n"); fclose($fp); } } $thread = new SimulatedThread(); $thread->simulate(); echo "Started to simulate threads...";
Summary of exploration: After running the above script, I found some things that were predictable but not what I had experienced. The results that come to mind
1. PHP-FPM configuration item pm.max_children = 5, simulated.thread record is as follows:
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)
Latest generated (Simulation) thread registration appears in the red marked entry position because the concurrent connection processing capacity of the process pool is limited to 5, so it can only appear in the sixth and subsequent positions.
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)
What’s interesting is that the registration time of the (simulated) thread represented by the green entry and the (simulated) thread represented by the red entry are the same, indicating that the two (simulated) threads are executed concurrently.
2. PHP-FPM configuration item pm.max_children = 10, simulated.thread record is as follows:
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)
Due to concurrent connection processing on the server side Capability is capped at 10, so newly spawned (simulated) thread registrations can appear anywhere.
3. Execute usleep(500) delay, simulated.thread record is as follows:
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)
Visible log recording sequence and (simulated) thread generation The order is consistent. The basic unit of usleep delay is microseconds (us, 1 s = 1000000 us).
It can be seen from the above records:
1) These (simulation) threads are automatically generated after the first request to execute the script. Yes, one (simulation) thread creates another (simulation) thread immediately;
2) Some of these (simulation) threads are generated and run in the same sub-process space;
3) The time interval between the generation of adjacent (simulated) threads is very small, almost at the same time, or the latter (simulated) thread is generated before the previous (simulated) thread has finished executing and exited;
4) Multiple (simulated) threads can execute concurrently.
So, the above implementation of simulating multi-thread concurrency is successful. The same sub-process in the PHP-FPM process pool can process multiple connection requests successively, but can only process one connection request at the same time. Unprocessed connection requests will enter the queue and wait for processing. In other words, the same child process does not have the ability to handle connection requests concurrently.
PHP-FPM Pool configuration: It allows the definition of multiple pools, and each pool can define different configuration items. The following is just a list of other configuration items that I paid attention to during the exploration process
1. listen: The address on which to accept FastCGI requests. It supports two communication protocols: TCP Socket and unix socket . You can set listen = [::]:9000.
2. listen.allowed_clients: List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. This configuration item is a comma-separated list, such as 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. This configuration item sets the way FPM manages the process pool, including static, dynamic, and 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. Set the upper limit of the number of requests processed by each child process, for processing Useful for memory leaks in third-party libraries.
5. pm.status_path: The URI to view the FPM status page.
The above is the detailed content of Exploration based on PHP-FPM process pool. For more information, please follow other related articles on the PHP Chinese website!