ホームページ  >  記事  >  バックエンド開発  >  スーパーバイザーを使用して Symfony コマンドの実行を処理する

スーパーバイザーを使用して Symfony コマンドの実行を処理する

WBOY
WBOYオリジナル
2024-09-07 06:34:021125ブラウズ

導入

この投稿では、supervisord を使用して symfony コマンドの実行を処理する方法を学びます。基本的に、supervisord により次のことが可能になります:

  • コマンドを自動開始します
  • コマンドを自動再起動します
  • スーパーバイザーに開始させるプロセスの数を指定します。

問題

プロセスの実行を自動化するために Unix の crontab を利用することがあります。ほとんどの場合、これで機能しますが、問題が発生する可能性があります。

ユーザーの通知を記録するデータベース テーブルがあると想像してみましょう。テーブルには次の情報が保存されます:

  • ユーザー
  • テキスト
  • チャンネル
  • ステータス (待機中、送信済み)
  • 作成場所
  • 更新日時

一方、次のステップに従って実行されるコマンドをコーディングしました。

  • 最後の WAITING 通知をクエリします
  • クエリされた通知をループし、次の処理を行います。
    • それぞれを対応するユーザーに送信します。
    • 通知ステータスを待機中から送信済みに更新します

Linux crontab でこのコマンドを頻繁に (1 分、2 分など) 実行するように設定しました。ここまでは順調です。

現在のプロセスが 500 件の通知をクエリし、400 件を送信したときに新しいプロセスが開始すると想像してみましょう。これは、新しいプロセスが、最後のプロセスによってまだ更新されていない 100 件の通知と新しい通知をクエリすることを意味します:

Using Supervisor to handle a Symfony Command execution

両方のプロセスが通知をクエリしているため、これらの 100 件の通知が 2 回送信される可能性があります。

解決策

解決策として、スーパーバイザーを使用することができます。これによりプロセスが実行され続け、必要に応じて再起動されます。このようにして、プロセスを 1 つだけ保持し、重複を避けます。コマンドがどのようになるべきかを分析してみましょう:

#[AsCommand(
    name: 'app:notification'
)]
class NotificationCommand extends Command
{
    private bool $forceFinish = false;

    protected function configure(): void
    {
        $this
            ->addOption('time-limit', null, InputOption::VALUE_OPTIONAL, 'Max time alive in seconds')
            ->addOption('time-between-calls', null, InputOption::VALUE_OPTIONAL, 'Time between every loop call')

        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->forceFinish = false;
        pcntl_signal(SIGTERM, [$this, 'signalHandler']);
        pcntl_signal(SIGINT, [$this, 'signalHandler']);

        $timeLimit = $input->getOption('time-limit');
        $timeBetweenCalls = $input->getOption('time-between-calls');
        $dtMax = (new \DateTimeImmutable())->add(\DateInterval::createFromDateString("+ {$timeLimit} seconds"));

        do{
           // Here we should execute a service to query and send notifications
           // ......

           sleep($timeBetweenCalls);
           $dtCurrent = new \DateTimeImmutable();

        }while($dtCurrent < $dtMax && !$this->forceFinish);

        return Command::SUCCESS;
    }

    public function signalHandler(int $signalNumber): void
    {
        echo 'Signal catch: ' . $signalNumber . PHP_EOL;
        match ($signalNumber) {
            SIGTERM, SIGINT => $this->forceFinish = true,
            default => null
        };
    }
}

コマンドをステップごとに説明してみましょう:

  • configure メソッドは、オプションを入力することを宣言します。

    • time-limit: コマンド プロセスが存続できる最大時間。その後、終了し、スーパーバイザーが再起動します。
    • time-between-calls: 各ループ反復後のスリープ時間。このループは、通知を処理するサービスを呼び出し、その間はスリープします。
  • execute メソッドは次のように動作します:

    • forceFinish クラス変数を true に設定します
    • PHP pnctl ライブラリを使用して、Unix SIGTERM および SIGINT シグナルを処理するためのメソッド signalHandler を登録します。
    • 入力オプション値を取得し、time-limit オプション値を使用するまでコマンドが有効である最大日付を計算します。
    • do-while ループは、通知を取得して送信するために必要なコードを実行します (コマンドには配置されず、代わりにコメントがあります)。その後、続行する前に、time-between-calls オプションで設定された時間スリープします。
    • 現在の日付 (ループの反復ごとに計算される) が最大日付より小さく、forceFinish が false の場合、ループは継続します。それ以外の場合、コマンドは終了します。
  • signalHandler 関数は、SIGTERM および SIGINT Unix シグナルをキャッチします。 SIGINT は Ctrl+C を押したときに送信されるシグナルであり、SIGTERM は kill コマンドを使用するときのデフォルトのシグナルです。 signalHandler 関数がそれらを検出すると、forceFinish 変数を true に設定します。これにより、forceFinish 変数が設定されているため、現在のループが終了するとコマンドが終了します。もはや偽りではありません。これにより、ユーザーは最大日付が終了するまで待つことなくプロセスを終了できます。

スーパーバイザの構成

これまでにコマンドを作成しました。次に、スーパーバイザがそれを処理できるようにセットアップします。設定を始める前に、スーパーバイザをインストールする必要があります。次のコマンドを実行して実行できます:

sudo apt update && sudo apt install supervisor

インストール後、次のコマンドを実行してスーパーバイザが実行されていることを確認できます。

sudo systemctl status supervisor

スーパーバイザー設定ファイルは、フォルダー /etc/supervisor/conf.d に配置されます。 notif.conf という名前のファイルを作成し、次の内容を貼り付けましょう:

command=php <your_project_folder>/bin/console app:notifications --time-limit=120 --time-between-calls=10
user=<your_user>
numprocs=1
autostart=true
autorestart=true
process_name=%(program_name)s_%(process_num)02d

各キーについて説明しましょう:

  • command: 開始するコマンド
  • user: コマンドを実行する UNIX ユーザー
  • numprocs: 実行するプロセスの数
  • autostart: コマンドを自動起動するかどうか
  • autostart: コマンドを自動再起動するかどうか
  • process_name: コマンド unix プロセス名の形式。

この構成では、app:notifications コマンドは最大 120 秒間実行され、ループごとに 10 秒間スリープします。 120 秒が経過するか、UNIX シグナルをキャッシュした後、コマンドはループを抜けて終了します。その後、スーパーバイザーが再度開始します。

結論

スーパーバイザーを使用して、crontab を使用せずにコマンドを実行し続ける方法を学習しました。これは、crontab によって起動されたプロセスが重複してデータ破損を引き起こす可能性がある場合に役立ちます。

私が書いた最後の本では、スーパーバイザーを使用して symfony メッセンジャーワーカーを実行し続ける方法を示しました。さらに詳しく知りたい場合は、次の書籍を参照してください: PHP と Symfony フレームワークを使用した操作指向の API の構築: ステップバイステップ ガイド

以上がスーパーバイザーを使用して Symfony コマンドの実行を処理するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。