【问题标题】:How to launch a Symfony Command asynchronously during a request using the Symfony Process component?如何在使用 Symfony Process 组件的请求期间异步启动 Symfony 命令?
【发布时间】:2021-11-10 16:26:46
【问题描述】:

我正在尝试使用 Symfony Process 组件执行 Symfony 命令,以便在收到 API 请求时异步执行。

当我这样做时,我收到 Code: 127(Command not found) 的错误消息,但是当我从控制台手动运行它时,它就像一个魅力。

这是电话:

public function asyncTriggerExportWitnesses(): bool
{
    $process = new Process(['php /var/www/bin/console app:excel:witness']);
    $process->setTimeout(600);
    $process->setOptions(['create_new_console' => true]);
    $process->start();
    $this->logInfo('pid witness export: ' . $process->getPid());
    if (!$process->isSuccessful()) {
        $this->logError('async witness export failed: ' . $process->getErrorOutput());
        throw new ProcessFailedException($process);
    }

    return true;
}

这是我得到的错误:

The command \"'php /var/www/bin/console app:excel:witness'\" failed.
Exit Code: 127(Command not found)
Working directory: /var/www/public
Output:================
Error Output:================
sh: exec: line 1: php /var/www/bin/console app:excel:witness: not found

我对 Process 组件的使用有什么问题?

这样调用也不行:

$process = new Process(['/usr/local/bin/php', '/var/www/bin/console', 'app:excel:witness']);

这会导致以下错误:

The command \"'/usr/local/bin/php' '/var/www/bin/console' 'app:excel:witness'\" failed.
Exit Code: ()
Working directory: /var/www/public
Output:
================
Error Output:
================

【问题讨论】:

  • 但它仍然没有回答为什么它不起作用。即使我只是调用“ls”,我也会收到反馈,但它失败了。

标签: php symfony symfony5 symfony-process


【解决方案1】:

首先,请注意,Process 组件并不意味着在父进程终止后异步运行。因此,在 API 请求期间触发异步作业运行并不是一个好的用例。

这两个cmetsin the docs关于异步运行的东西很中肯:

如果在子进程有机会完成之前发送了响应,则服务器进程将被终止(取决于您的操作系统)。这意味着您的任务将立即停止。运行异步进程与运行在其父进程中存在的进程不同。

如果您希望您的进程在请求/响应周期中存活下来,您可以利用 kernel.terminate 事件,并在此事件中同步运行您的命令。请注意,仅当您使用 PHP-FPM 时才会调用 kernel.terminate。

还要注意,如果你这样做了,在子进程完成之前,上述 PHP-FPM 进程将无法为任何新请求提供服务。这意味着如果您不够小心,您可以快速阻止您的 FPM 池。这就是为什么即使在发送请求后最好不要做任何花哨的事情,而是使用作业队列来代替。

如果您想异步运行作业,只需将作业存储在“某处”(编辑数据库、redis、文本文件等),然后让解耦的消费者通过“待处理作业”并执行您需要的任何操作而无需触发API 请求中的作业。

上面的这个很容易实现,但是你也可以使用 Symfony Messenger 来为你做这件事。根据您的 API 请求调度消息,与您的作业队列使用者一起使用消息。


话虽如此,您对进程的使用也失败了,因为您正在尝试混合同步和异步方法。

您第二次调用该命令的尝试至少成功地找到了可执行文件,但因为您在作业完成之前调用了isSuccessful()

如果你使用start()(而不是run()),你不能简单地直接调用isSuccessful(),因为工作还没有完成。

以下是执行异步作业的方法(尽管这在 API 请求期间很少有用):

class ProcessCommand extends Command
{

    protected static $defaultName = 'process_bg';

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $phpBinaryFinder = new PhpExecutableFinder();

        $pr = new Process([$phpBinaryFinder->find(), 'bin/console', 'bg']);
        $pr->setWorkingDirectory(__DIR__ . '/../..');

        $pr->start();

        while ($pr->isRunning()) {
            $output->write('.');
        }
        $output->writeln('');

        if ( ! $pr->isSuccessful()) {
            $output->writeln('Error!!!');

            return self::FAILURE;
        }

        $output->writeln('Job finished');

        return self::SUCCESS;
    }

}

【讨论】:

  • 非常感谢,这真的很容易!我以前从未使用过 Messenger,它就像一个魅力!
猜你喜欢
  • 2018-08-25
  • 2015-02-20
  • 1970-01-01
  • 2022-12-01
  • 2017-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多