PHP はマルチプロセスをサポートしますが、マルチスレッドはサポートしません。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
リストからわかるように、プロセス内に 2 つのアイドル状態の子プロセス PID 8 と PID があります。プールwww 9.注: NLWP は、軽量プロセスの数、つまりスレッドの数を指します。
PHP-FPM (FastCGI プロセス マネージャー) とは何ですか? PHP-FPM は、メモリとプロセスを効果的に制御し、PHP 設定をスムーズにリロードできる PHP-CGI のプロセス管理メソッドを提供し、そのマスター プロセスはメモリ上に常駐します。 FastCGI は、言語に依存せず、スケーラブルなアーキテクチャの CGI オープン拡張機能であり、その主な動作は、フォークして実行するのではなく、CGI インタープリタ プロセスをメモリ内に長時間保持することで、より高いパフォーマンスを実現します。 FastCGI は分散配置をサポートしており、WEB サーバー以外の複数のホストに配置できます。
探索する方法: マルチスレッドの同時実行のシミュレーション
関連する学習の推奨事項:PHP プログラミングの入門から熟練度まで
1. スレッドとは: スレッドは軽量プロセス (LWP) と呼ばれることもあります。通常、スレッド ID、現在の命令ポインター (PC)、レジスタ セットで構成されます。プロセス内のエンティティは、システムによって独立してスケジュールされる基本単位であり、スレッド自体はシステム リソースを所有せず、動作に必要な一部のリソースのみを所有し、プロセスが所有するすべてのリソースを他のスレッドと共有します。同じプロセスに属します。スレッド間の相互制約により、スレッドの動作に不連続性が見られます。スレッドには、準備完了、ブロック済み、実行中の 3 つの基本状態もあります。プロセスがリソース所有者であるため、作成、キャンセル、切り替えのオーバーヘッドが高すぎるため、対称型マルチプロセッサ (SMP) 上で複数のスレッド (スレッド) を同時に実行する方が適切な選択です。スレッドの実体には、プログラム、データ、およびスレッド制御ブロック (TCB) が含まれます。TCB には次の情報が含まれます:
(1) スレッドのステータス;
(2) スレッドが実行されたとき非実行時に保存されるオンサイトのリソース;
(3) 実行スタックのセット;
(4) 各スレッドのローカル変数を格納するメイン メモリ。
(5) 同じプロセス内のメインメモリとその他のリソースにアクセスします。
ただし、複数のプロセスを使用すると、プロセス プール内のプロセスがクラッシュしたり攻撃された場合にアプリケーションがより堅牢になります。
2. マルチスレッドのシミュレート:
<?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...";
調査の概要: 上記のスクリプトを実行した後、予測可能なことがいくつか見つかりましたが、思い浮かぶ結果
#1. PHP-FPM 設定項目 pm.max_children = 5、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)
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)
2. PHP-FPM 設定項目 pm.max_children = 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)
3. usleep(500) 遅延を実行します。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)
上記のレコードからわかります:
1) これらの (シミュレーション) スレッドは、最初のリクエストを実行した後に自動的に生成されます。はい、1 つの (シミュレーション) スレッドがすぐに別の (シミュレーション) スレッドを作成します;2) これらの (シミュレーション) スレッドの一部は生成され、同じサブプロセス空間で実行されます; 3) 隣接する (シミュレートされた) スレッドの生成間の時間間隔が非常に短く、ほぼ同時に、または前の (シミュレートされた) スレッドが実行を終了して終了する前に後者の (シミュレートされた) スレッドが生成されます。 ##4) 複数の (シミュレートされた) スレッドを同時に実行できます。 したがって、マルチスレッド同時実行をシミュレートする上記の実装は成功しました。 PHP-FPM プロセス プール内の同じサブプロセスは、複数の接続リクエストを連続して処理できますが、同時に処理できる接続リクエストは 1 つだけです。未処理の接続リクエストはキューに入り、処理を待ちます。つまり、同じ子プロセスには接続要求を同時に処理する機能がありません。 PHP-FPM プール構成: 複数のプールを定義でき、各プールは異なる構成項目を定義できます。以下は、調査プロセス中に私が注意を払った他の構成項目の単なるリストです。1. listen: FastCGI リクエストを受け入れるアドレス。TCP ソケットと TCP ソケットの 2 つの通信プロトコルをサポートします。 unix ソケット。 listen = [::]:9000 と設定できます。
2. listen.allowed_clients: 接続を許可される FastCGI クライアントのアドレス (IPv4/IPv6) のリスト この設定項目は、listen.allowed_clients = 127.0.0.1,172.17.0.5 のようなカンマ区切りのリストです。
3. pm: プロセス マネージャーが子プロセスの数を制御する方法を選択します。この構成項目は、FPM が静的、動的、オンデマンドなどのプロセス プールを管理する方法を設定します。
4. pm.max_requests: 各子プロセスが再生成する前に実行するリクエストの数。これは、サードパーティのライブラリでのメモリ リークを回避するのに役立ちます。それぞれが処理するリクエストの数の上限を設定します。子プロセス、処理用 サードパーティ ライブラリのメモリ リークに役立ちます。
5. pm.status_path: FPM ステータス ページを表示するための URI。
以上がPHP-FPMプロセスプールに基づく探索の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。