Exploration of Process Pool Based on PHP FPM

  • 2021-08-10 06:54:57
  • OfStack

PHP supports multiple processes but not multiple threads; PHP-FPM runs multiple child processes in the process pool to process all connection requests concurrently. View the status of the PHP-FPM process pool (pm.start_servers = 2) via 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 you can see from the list, there are two idle child processes PID 8 and PID 9 in the process pool www. 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 process management mode for PHP-CGI, which can effectively control memory and process, smoothly overload PHP configuration, and its master process is memory resident. FastCGI is an open extension of CGI with a language-independent, scalable architecture, and its main behavior is to keep the CGI interpreter process in memory longer than fork-and-execute, and thus achieve higher performance. FastCGI supports distributed deployment and can be deployed on multiple hosts other than WEB servers.

Exploring means: Simulating multithreaded concurrent execution

1. What is a thread: A thread is sometimes called a lightweight process (Lightweight Process, LWP), which is usually composed of a thread ID, a current instruction pointer (PC), a register set and a stack. It is an entity in the process and a basic unit independently scheduled by the system; Threads do not own system resources, but only have 1 point of essential resources in operation, and share all the resources owned by the process with other threads belonging to the same process. Because of the mutual restriction between threads, threads are discontinuous in running. Threads also have three basic states: ready, blocked and running. Because the process is the resource owner, the overhead of creating, undoing and switching is too high, so running multiple threads (Threads) on symmetric multiprocessor (SMP) at the same time is a more suitable choice. The entities of the thread include programs, data, and thread control blocks (Thread Control Block, TCB), TCB including the following information:

(1) Thread state;

(2) When the thread is not running, the saved field resources;

(3) One set of execution stacks;

(4) Local variable main memory for each thread;

(5) Access main memory and other resources in the same process.

But using multiple processes can make your application more robust in the event that a process in the process pool crashes or is attacked.

2. Simulate multithreading:


<?php
/**
 * PHP  Support multi-process only, not multi-thread. 
 *
 * PHP-FPM  Run multiple child processes in the process pool to process all connections concurrently, 
 *  Same as 1 Child processes can process multiple connection requests in succession, but the same as 1 Time 
 *  Can only handle 1 Unprocessed connection requests will be queued for processing 
 *
 */

class SimulatedThread
{
 // Analog thread 
 private $thread;

 // Hostname 
 private $host = 'tcp://172.17.0.5';

 // Port number 
 private $port = 80;

 public function __construct()
 {
  // Number threads with current time 
  $this->thread = microtime(true);
 }

 /**
  *  Pass socket Send 1 A new one HTTP Connect the request to this machine, 
  *  At this point, the current impersonation thread is both a server and an impersonation client 
  *
  *  Current ( Program ) Child process sleep(1) It will be delayed later 1s But the connection it holds continues to be valid, 
  *  Can't process new connection requests, so this will reduce the ability of process pools to handle concurrent connection requests, 
  *  Similar delay processing includes time_nanosleep() , time_sleep_until() , usleep() . 
  *  And sleep(1) This practice is not safe, nginx The following errors may still occur: 
  *  " 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) {// Maximum simulation 10 Threads 
   $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();
 }

 /**
  *  Log the running time of the current impersonated thread 
  *
  * @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...";

Inquiry summary: After running the above script, I found 1 predictable but not what I had thought of.

1. PHP-FPM configuration entry pm.max_children = 5, simulated. thread recorded 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)

The newly generated (impersonated) thread enlistment appears at the red entry because the process pool's concurrent connection processing capacity is capped at 5, so it can only appear after Article 6.


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)

Interestingly, the registration time of the (impersonated) thread represented by the green entry and the (impersonated) thread represented by the red entry is 1, indicating that the two (impersonated) threads are executed concurrently.

2. PHP-FPM configuration entry pm.max_children = 10, simulated. thread recorded 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)

Since the server-side concurrent connection processing capacity is capped at 10, the newly generated (impersonated) thread enlistment can appear anywhere.

3. Perform usleep (500) delay, simulated. thread records 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)

It can be seen that the logging order is consistent with the (simulated) thread generation order. The basic unit of usleep delay is subtlety (us, 1 s = 1000000 us).

As can be seen from the above records:

1) These (mock) threads are automatically generated after the first request to execute the script, and one (mock) thread creates another (mock) thread immediately;

2) Some of these (simulated) threads are generated and run in the same sub-process space;

3) The generation time interval of adjacent (simulated) threads before and after is very small, which is almost generated at the same time, or the last (simulated) thread is generated before the first (simulated) thread is executed and exited;

4) Multiple (impersonated) threads can execute concurrently.

Therefore, the above implementation of simulating multithreading concurrency is successful. In PHP-FPM process pool, the same sub-process can process multiple connection requests successively, but only one connection request can be processed in the same time, and unprocessed connection requests will enter the queue and wait for processing. In other words, the same child process does not have the ability to process connection requests concurrently.

PHP-FPM Pool Configuration: It allows you to define multiple pools, each with a different configuration entry. The following is just a list of other configuration items that I have paid attention to during the exploration

1. listen: The address on which to accept FastCGI requests. It supports TCP Socket and unix socket communication protocols. You can set listen = [::]: 9000.

2. listen.allowed_clients: List of addresses (IPv4/IPv6) of FastCGI clients 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 how FPM manages process pools, 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 in in in in in in in

5. pm.status_path: The URI to view the FPM status page.


Related articles: