php - 如何在使用 Symfony Process 组件的请求期间异步启动 Symfony 命令?
问题描述
我正在尝试使用 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:
================
解决方案
首先,请注意 Process 组件并不意味着在父进程死亡后异步运行。因此,在 API 请求期间触发异步作业运行并不是一个好的用例。
文档中关于异步运行的这两条评论非常相关:
如果在子进程有机会完成之前发送了响应,则服务器进程将被终止(取决于您的操作系统)。这意味着您的任务将立即停止。运行异步进程与运行在其父进程中幸存的进程不同。
如果您希望您的进程在请求/响应周期中存活,您可以利用 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;
}
}
推荐阅读
- c++ - CMake cpprestsdk
- java - 无论如何,是否可以使用 Java 代码从“PUBmed”文章中获取完整摘要
- javascript - JavaScript如何用另一个数组的每个元素替换数组的第一个元素
- amazon-web-services - 无法在 AWS 上设置 HashiCorp Vault
- node.js - Office 365 POP 提取邮件出现间歇性错误登录失败:未知用户名或密码错误
- ruby - 如何使用 unixODBC 从 DB2 V11.5 Linux 上的表 SYSIBM.SQLCOLUMNS 获取 column_def 值 - Ruby
- android - 单击登录按钮时应用程序崩溃(简单登录注册测试)
- spring-boot - Spring thymeleaf 模板引擎找不到模板文件
- php - elFinder - 如何从 AWS S3 存储桶打开/显示特定文件夹
- jquery - Ajax 请求正常,但在错误响应文本中接收数据