blog

PHPの開発者は、マルチプロセスの消費キューを知っておくべきである

はじめに 最近、キューmcqを使用して、小さな機能を開発し、キューデータを消費するプロセスを開始し、後でプロセスがオーバー処理できないことが判明し、プロセスを追加し、一定期間後に、オーバー処理すること...

Sep 25, 2020 · 10 min. read
シェア

はじめに

最近、小さな機能を開発し、キューmcqを使用して、キューデータを消費するプロセスを開始し、後でプロセスがオーバー処理できないことが判明し、プロセスを追加し、一定期間後に、オーバー処理することはできません...

プロセスがハングアップした場合、この方法は、クーロンタブを変更するたびに、時間内に開始されません、次の時間までクーロンタブの実行が開始されます。スリーププロセスは、処理ロジックであると仮定し、次の例のように、処理中のデータを失う可能性がありますkillでプロセスを閉じ、ここで明確に処理時間の効果を見るために10秒に拡大されます:

<?php
$i = 1;
while (1) {
 echo " [{$i}] 
";
 sleep(10);
 echo " [{$i}] 
";
 $i++;
}

スクリプトを実行し、ループが開始された後まで待った後、 kill {KaTeX parse error: Expected 'EOF', got '}' at position 4: pid}̲ をプロセスに送ると、 デフォルトではSI番号15...iがキューから取得しており、 2になると処理中で、killシグナルをプログラムがkillシグナルを送るので、キューからデータが失われるのと同様にもっと問題があるので、これらの問題を解決する方法を考えなければなりません。

 [1] 
 [1] 
 [2] 
[1] 28372 terminated php t.php

nginxプロセスモデル

そこで思いついたのが、何千もの企業や個人にサービスを提供する高性能サーバーの主力であるnginxの、より古典的なプロセスモデルです:

管理者はマスタプロセスを通してnginxと対話し、/path/to/nginx.pidからnginxマスタプロセスのpidを読み込み、マスタプロセスにシグナルを送り、マスタは異なるシグナルに応じて異なる処理を行い、管理者に情報をフィードバックします。マスターはworkerを管理する責任があり、ビジネスに対処するためではなく、workerは特定のビジネスプロセッサであり、マスターはworkerの終了を制御することができます、開始、workerが誤って終了したとき、マスターはサブプロセスの終了のメッセージを受信しますが、また、ビジネス処理に影響を与えないように、新しいworkerプロセスを追加して再起動します。nginxはまた、終了を滑らかにすることができます処理されるデータのいずれかを失うことはない、nginxの設定を更新するオンラインサービスに影響を与えることなく行うことができます新しい設定をロードするために、これは、特にリクエストの数が多い場合に便利です。

プロセス設計

モデルにnginxを見て、それは、単一のファイルは、すべてのプロセスを制御するために行うには、mcqのデータを処理のニーズを満たすために、同様のクラスライブラリを開発することが可能です、あなたがスムーズに終了することができます、あなたは子プロセスの状態を表示することができます。キューデータの処理は、一定の遅延を受信するため、あまりにも複雑にする必要はありません、中断されないサービスとしてnginxを行うには、より面倒な、時間のかかる面倒な、意義は非常に大きくありません。プロセスモデルの設計は、よりnginxの簡易版のようなnginxに似ています。

プロセス信号設計

信号は、プロセス間の通信の方法であり、比較的単純な、単一の機能も比較的弱いですが、唯一のプロセスに信号を送信することができ、プロセスは別の処理を行うための信号に応じて。

マスタープロセスは起動時に/path/to/daeminze.pidというファイルにpidを保存し、管理者はシグナルを通じてマスタープロセスと通信します。 マスタープロセスは3種類のシグナルをインストールし、以下のように異なるシグナルに遭遇し、異なる処理を行います:

SIGINT 	=> スムーズな終了、処理中のデータを処理した後に終了する。
SIGTERM => 暴力的な終了に関係なく、プロセスが直接終了データを処理しているかどうか
SIGUSR1 => プロセスステータス、プロセスメモリ、ランタイム、およびその他の情報を表示する。

マスタープロセスはワーカープロセスとシグナルで通信し、ワーカープロセスは以下のように2つのシグナルを設置します:

SIGINT 	=> スムーズな終了
SIGUSR1	=> ワーカープロセスの状態を確認する

なぜワーカープロセスは2つのシグナルしかインストールしないのですか? SIGTERMが少ないのは、マスタープロセスがシグナルSIGTERMを受信した後、ワーカープロセスにSIGKILLシグナルを送り、デフォルトでプロセスを強制終了させるからです。

ワーカープロセスはマスタープロセスによってフォークされ、マスタープロセスは pcntl_wait によって子プロセスの終了イベントを待ち、子プロセスが終了したときに子プロセスの pid を返し、処理を行い、新しいプロセスを起動して補充します。

また、マスター・プロセスはpcntl_waitを介してシグナル受信を待ち、シグナルが到着すると-1を返します。 この領域にはいくつかの落とし穴があり、以下で詳しく説明します。

最初の方法は declare(ticks = 1); で、これはあまり効率的ではありません。 Zend は、低レベルのステートメントを実行するたびに プロセス内に未処理のシグナルがあるかどうかをチェックします。

もうひとつは pcntl_signal_dispatch を使って未処理のシグナルをコールする方法で、これは PHP 5.4.0 以降に適用可能です。

PHPシグナルリペアのインストール

PHP は pcntl_signal を使ってシグナルをインストールします:

bool pcntl_signal ( int $signo , [callback $handler [, bool $restart_syscalls = true ] )

3番目のパラメータrestart_syscallsはよく理解されていない、多くの情報を探して、あまりにも多くのことを見つけるために、テストがpcntl_wait関数でこのパラメータは、信号を受信することが影響を与えることが判明した後、trueのデフォルト値に設定すると、pcntl_waitを持つプロセスに信号を送信すると、受信することはできません、それは順序でfalseに設定する必要があります次のように表示することができます。例

<?php
$i = 0;
while ($i<5) {
 $pid = pcntl_fork();
 $random = rand(10, 50);
 if ($pid == 0) {
 sleep($random);
 exit();
 }
 echo "child {$pid} sleep {$random}
";
 $i++;
}
pcntl_signal(SIGINT, function($signo) {
 echo "Ctrl + C
";
});
while (1) {
 $pid = pcntl_wait($status);
 var_dump($pid);
 pcntl_signal_dispatch();
}

実行後、親プロセスにkill -SIGINT {$pid}シグナルを送信し、pcntl_waitが応答しないことを確認し、子プロセスが終了するまで待ち、送信されたSIGINTは、次の結果のように、一つずつ実行されます:

child 29643 sleep 48
child 29644 sleep 24
child 29645 sleep 37
child 29646 sleep 20
child 29647 sleep 31
int(29643)
Ctrl + C
Ctrl + C
Ctrl + C
Ctrl + C
int(29646)

これは、スクリプトの実行直後に親プロセスに送られる4つのSIGINTシグナルで、子プロセスが起動するまでに、すべてのシグナルがトリガーされます。

ただし、設置信号の第3パラメータがfalseに設定されている場合:

pcntl_signal(SIGINT, function($signo) {
 echo "Ctrl + C
";
}, false);

この時点で、親プロセスにSIGINTシグナルを送ると、pcntl_waitは直ちに-1を返し、シグナルに対応するイベントがトリガーされます。

つまり、3番目のパラメータは、このシグナルを再登録するかどうかを意味しているのでしょう。もしfalseであれば、一度だけ登録し、トリガー後に返せば、pcntl_waitはメッセージを受け取ることができます。

シグナルとシステムコール

シグナルはシステムコールを中断し、システムコールがすぐに戻るようにします。例えば、プロセスがスリープ中にシグナルを受け取ると、sleepはスリープの残り秒数などをすぐに返します:

<?php
pcntl_signal(SIGINT, function($signo) {
 echo "Ctrl + C
";
}, false);
while (true) {
	pcntl_signal_dispatch();
 echo "123
";
 $limit = sleep(2);
	echo "limit sleep [{$limit}] s
";
}

実行後、Ctrl + Cを押します:

123
^Climit sleep [1] s
Ctrl + C
123
limit sleep [0] s
123
^Climit sleep [1] s
Ctrl + C
123
^Climit sleep [2] s

daemon

この種のプロセスは一般的にデーモンプロセスとして設計されており、ターミナルによって制御されず、ターミナルと相互作用せず、バックグラウンドで長時間実行されます:

protected function daemonize()
{
 $pid = pcntl_fork();
 if (-1 == $pid) {
 throw new Exception("forkプロセスの失敗");
 } elseif ($pid != 0) {
 exit(0);
 }
 if (-1 == posix_setsid()) {
 throw new Exception("新しいセッションの作成に失敗しました");
 }
 $pid = pcntl_fork();
 if (-1 == $pid) {
 throw new Exception("forkプロセスの失敗");
 } else if($pid != 0) {
 exit(0);
 }
 umask(0);
 chdir("/");
}

プロセスには5つのステップがあります:

  1. は子プロセスをフォークし、親プロセスは終了します。
  2. 子プロセスをセッションリーダーおよびプロセスリーダーに設定します。
  3. フォークが再び発生すると、親プロセスは終了し、子プロセスは実行し続けます。
  4. リカバリーファイルのマスクは 0 です。
  5. カレント・ディレクトリをルート・ディレクトリ / に切り替えます。

ステップ2はステップ1の準備で、プロセスをセッションリーダーに設定します。必要な条件はプロセスがプロセスリーダーでないことなので、最初のフォークを行い、プロセスリーダーは終了し、子プロセスはposix_setsid()を介してセッションリーダーとして設定され、プロセスリーダーとしても設定されます。

ステップ3は、プロセスが端末を制御するための必要条件はセッションリーダーであるため、プロセスが端末の制御を取り戻さないようにすることです。

ステップ4は、ファイルマスクを設定し、不要なトラブルをもたらす操作を行うことを避けるために、デフォルトのファイルマスクを復元することです。ファイルマスクについては、Linuxでは、ファイルマスクは、ファイルやフォルダを作成するときに使用され、ファイルのデフォルトのアクセス許可は666ですが、フォルダは777ですが、デフォルトの値は、ファイルを作成する最終的な値としてマスクの値を差し引いたファイルを作成するために使用されます、例えば、マスク022の下に666ファイルを作成する - 222 = 644は、フォルダ777を作成する - 022 = 755:

umask(0)666777
umask(022)644755

ステップ5は、ルートディレクトリ/にカレントディレクトリを切り替えることですが、インターネットでは、彼のディレクトリが正しくアンインストールすることはできません実行を開始しないようにするために、これはあまりにも理解していないと言います。

5つのステップに対応し、各ステップごとに様々なID変更情報があります:

開始
最初のフォーク。1
posix_setsid()1
セカンドフォーク1

また、セッション、プロセス・グループ、およびプロセスの関係を以下の図に示します。

この時点で、簡単にデーモンプロセスを作ることもできます。

コマンドデザイン

私はこのクラス・ライブラリのために、以下のように6つのコマンドを設計するつもりです:

スタートアップコマンド

スタートアップ・コマンドは、pidファイルに既にpidが存在するかどうか、そのpidに対応するプロセスが健全かどうか、再起動が必要かどうかを検出します。

強制停止コマンド

管理者は、pid と組み合わせてエントリーファイルを介してマスタープロセスに SIGTERM シグナルを送り、マスタープロセスはすべての子プロセスに SIGKILL シグナルを送り、マスタープロセスも終了する前にすべてのワーカープロセスが終了するのを待ちます。

強制再起動コマンド

強制停止コマンド+スタートコマンド

スムーズな停止コマンド

スムーズな停止コマンド、管理者はマスタープロセスに SIGINT シグナルを送り、マスタープロセスは全ての子プロセスに SIGINT を送り、ワーカープロセスはその状態を停止としてマークし、ワーカープロセスが次にループするとき、停止決定に従って停止を決定し、新しいデータを受け取らないようにします。全てのワーカープロセスが終了すると、マスタープロセスも終了します。

スムーズな再起動コマンド

スムーズストップコマンド+スタートコマンド

プロセスステータスの表示

workermanのアイデアから借りて、このプロセスのステータスを表示するには、管理者がマスタープロセスにSIGUSR1信号を送信し、マスタープロセスに伝える、私はすべてのプロセス、マスタープロセスの情報を参照したい、マスタープロセスは、設定されたファイルパスAに独自のプロセス情報を書き込みますし、SIGUSR1を送信し、ワーカープロセスに独自の情報を書き込むように指示します。このプロセスは非同期であるため、ワーカープロセスは、書き込みが終了したときにわからないので、マスタプロセスは、ここで待機し、すべてのワーカープロセスがファイルに書き込まれるように、すべての情報の出力をフォーマットし、最終的な出力は以下のとおりです:

➜/dir /usr/local/bin/php DaemonMphp status
Daemon [DaemonM  :
-------------------------------- masterプロセスステータス
pid メモリ使用量 処理時間 開始時間 実行時間
16343 0.75M -- 2018-05-15 09:42:45 0 日 0時間 3分
12 slaver
-------------------------------- slaverプロセスステータス
タスク task-mcq:
16345 0.75M 236 2018-05-15 09:42:45 0 日 0時間 3分
16346 0.75M 236 2018-05-15 09:42:45 0 日 0時間 3分
--------------------------------------------------------------------------------
タスク test-mcq:
16348 0.75M 49 2018-05-15 09:42:45 0 日 0時間 3分
16350 0.75M 49 2018-05-15 09:42:45 0 日 0時間 3分
16358 0.75M 49 2018-05-15 09:42:45 0 日 0時間 3分
16449 0.75M 1 2018-05-15 09:46:40 0 日 0時間 0分
--------------------------------------------------------------------------------

ワーカープロセスがファイルにプロセス情報を書き込むのを待ちます。ここはトリックメソッドを使用して、各ワーカープロセスは、ファイルの行数をカウントするために情報の行を出力し、ワーカープロセスの行数に達した後、それはすべてのワーカープロセスが最後まで情報を書き込むことを意味し、そうでない場合は、1秒ごとに検出します。

その他

さらに実用的な機能として、ワーカープロセスの実行時間の制限と、ワーカープロセスのループ処理回数の制限が追加され、長時間のループ処理によるメモリオーバーなどの不測の事態を防ぐことができるようになりました。デフォルトの時間は 1 時間で、デフォルトの実行回数は 10w 回です。

また、マルチタスクをサポートすることができ、各タスクは、マスタープロセス管理によって統一され、独立して開いているいくつかのプロセス。

Read next

React Advanced - HOC

コンセプトは、引数がコンポーネントで、戻り値が新しいコンポーネントである関数です チェーンで呼び出すことができます デコレーターとして書くことができます 基本的な使い方の注意事項 レンダーメソッドでHOCを使用しないでください

Sep 24, 2020 · 1 min read