使用 Supervisor 處理 Symfony 指令執行

WBOY
發布: 2024-09-07 06:34:02
原創
1119 人瀏覽過

介紹

在這篇文章中,我們將學習如何使用supervisord來處理symfony指令的執行。基本上,supervisord 將允許我們:

  • 自動啟動指令
  • 自動重新啟動指令
  • 指定我們希望主管啟動的進程數。

問題

有時我們會使用 unix crontab 來自動執行進程。這可能在大多數情況下有效,但在某些情況下可能會導致問題。

假設我們有一個記錄使用者通知的資料庫表。此表儲存以下資訊:

  • 使用者
  • 文字
  • 頻道
  • 狀態(等待,已發送)
  • 創建於
  • 更新於

另一方面,我們編寫了一個命令,其執行遵循以下步驟:

  • 查詢最近的WAITING通知
  • 循環查詢的通知並:
    • 將每一個傳送給對應的使用者。
    • 將通知狀態從 WAITING 更新為 SENT

我們在 Linux crontab 中設定此命令每隔一段時間運行一次(1 分鐘、2 分鐘等)。到目前為止一切順利。

現在假設當前進程已查詢 500 個通知,當發送 400 個通知時,一個新進程啟動。這意味著新進程將查詢上一個進程尚未更新的 100 條通知以及新的通知:

Using Supervisor to handle a Symfony Command execution

這可能會導致這 100 個通知發送兩次,因為兩個進程都查詢了它們。

解決方案

作為解決方案,我們可以求助於使用supervisor。它將保持我們的進程運行並在需要時重新啟動它。這樣,我們只保留一個進程並避免重疊。讓我們來分析一下指令應該是什麼樣子:

#[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- Between-calls:每次循環迭代後的睡眠時間。此循環呼叫處理通知的服務,然後在此期間休眠。
  • execute 方法的行為如下:

    • forceFinish 類別變數設為 true
    • 使用 PHP pnctl 函式庫註冊方法 signalHandler 來處理 Unix SIGTERMSIGINT 訊號​​。
    • 取得輸入選項值並計算指令可以有效的最大日期,直到使用時間限制選項值。
    • do-while 循環執行所需的程式碼來取得通知並發送通知(它沒有放在命令中,而是有註解)。然後,它會休眠由 time- Between-calls 選項建立的時間,然後再繼續。
    • 如果當前日期(在每次循環迭代中計算)低於最大日期並且 forceFinish 為 false,則循環繼續。否則命令完成。
  • signalHandler 函式擷取 SIGTERM 和 SIGINT Unix 訊號。 SIGINT是當我們按下Ctrl+C時發送的訊號,SIGTERM是當我們使用kill指令時的預設訊號。當signalHandler 函數偵測到它們時,它會將forceFinish 變數設為true,這樣,當當前循環完成時,命令將完成,因為forceFinish 變數為不再虛假。這允許用戶終止進程,而不必等到最大日期完成。

配置主管

到目前為止,我們已經建立了命令。現在是時候設定主管了,以便它可以處理它。在開始設定之前,我們必須安裝supervisor。您可以執行以下命令來完成此操作:

sudo apt update && sudo apt install supervisor
登入後複製

安裝後,您可以透過執行以下命令來確保supervisor正在運行:

sudo systemctl status supervisor
登入後複製

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
登入後複製

讓我們解釋一下每個鍵:

  • 指令:啟動指令
  • user:執行指令的unix使用者
  • numprocs:要運行的進程數
  • autostart:是否自動啟動指令
  • autostart:是否自動重啟指令
  • process_name:指令unix行程名稱格式。

使用此配置,app:notifications 命令將運行最多 120 秒,並且在每次循環後它將休眠 10 秒。經過 120 秒或快取 unix 訊號後,該指令將退出循環並完成。然後,主管將再次啟動。

結論

我們已經學會如何使用supervisor來保持命令運行而無需使用crontab。當 crontab 啟動的進程可能重疊並導致資料損壞時,這會很有用。

在我寫的上一本書中,我展示瞭如何使用 Supervisor 來保持 symfony Messenger Worker 的運作。如果您想了解更多信息,可以在這裡找到這本書:使用 PHP 和 Symfony 框架構建面向操作的 Api:分步指南

以上是使用 Supervisor 處理 Symfony 指令執行的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板