使用Symfony进程运行后台任务,而无需等待进程完成



用户提交表单后,我想呈现一个视图文件,然后我想启动一个后台任务来处理五个MS Excel文件(每个文件最多可以有2000行),但这样用户就不必等待流程完成才能查看页面。任务完成后,我会通过电子邮件通知用户。

我使用的是Symfony Framework 3。我在下面包含了我的代码。它并没有实现我想要实现的目标。提交表单后,只有在整个后台任务完成时才会加载页面。

我不确定,但在谷歌上搜索了很多之后,我认为kernel.terminate事件可能在这里有用。但我似乎不明白如何使用它。

你能告诉我如何解决这个问题吗?

这是我的代码:

我创建了一个控制台命令:

class GreetCommand extends ContainerAwareCommand {
protected function configure()
{
$this->setName('hello:world');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// My code to execute
}
}

我的控制器里有

$process = new Process('ls -lsa');
$process->disableOutput();
$that = $this;
$process->start(function ($type, $buffer) use ($that) {
$command = new GreetCommand();
$command->setContainer($this->container);
$input = new ArrayInput(array());
$output = new NullOutput;
$resultCode = $command->run($input, $output);
});
return $this->render('success.html.php',  array('msg' => 'Registraion Successful'));

更新

我已经使用PHP的连接处理功能解决了这个问题。

感谢这篇文章

异步运行进程

您也可以启动子流程,然后让它异步运行,每当您需要它。使用start()方法启动异步进程

文档

因此,要异步启动您的命令,您应该使用命令创建新的进程并启动

$process = new Process('php bin/console hello:word');
$process->start();

考虑将其更改为完整路径,如usrbinphp varwwwhtmlbinconsole hello:word

还有一个很好的捆绑包cocur/background进程,你可以使用它,或者至少阅读文档来了解它是如何工作的。

我玩游戏有点晚了,但我刚刚使用fromShellCommandLine()方法找到了这个问题的解决方案:

use SymfonyComponentProcessProcess;
Process::fromShellCommandline('/usr/bin/php /var/www/bin/console hello:world')->start();

通过这种方式,可以异步启动新进程/运行命令。

用于控制器:

use SymfonyComponentHttpKernelKernelEvents;
use SymfonyComponentHttpKernelEventPostResponseEvent;
$myVar = new MyObject();
$this->get('event_dispatcher')->addListener(KernelEvents::TERMINATE, function(PostResponseEvent $event) use($myVar) {
//You logic here
$request = $event->getRequest();
$test = $myVar->getMyStuff();
});

但这不是一个好的做法,请阅读有关正常注册事件侦听器的信息

kernel.terminate事件将在向用户发送响应后发送。

有一个名为AsyncServiceCallBundle的捆绑包,它允许您在后台调用服务的方法。

你可以参考这个答案来了解更多关于如何在内部完成的细节。您所需要的一切都是调用服务的方法,如下所示:

$this->get('krlove.async')->call('service_id', 'method', [$arg1, $arg2]);

简单使用Symfony过程选项

create_new_console

来自Symfony流程来源:

/**
* Defines options to pass to the underlying proc_open().
*
* @see https://php.net/proc_open for the options supported by PHP.
*
* Enabling the "create_new_console" option allows a subprocess to continue
* to run after the main process exited, on both Windows and *nix
*/
public function setOptions(array $options)

所以让我们这样做:

$process = new Process($cmds);
$process->setOptions(['create_new_console' => true]);
$process->start();

我想您想要的是将必要的数据存储在数据库中,然后让cronjob/队列执行实际命令,而不是尝试直接从控制器执行它。

在/etc/crontab中添加以下内容,使其每5分钟运行一次您的命令

*/5 * * * * root /usr/bin/php /path/to/bin/console hello:world

然后,让您的命令查询数据库中存储的数据,并让它处理excel文件

尽管它已经得到了回答,
如果有人需要,我会在这里发布我的工作解决方案
在我的控制器中

public function exportDossiersAction(Request $request)
{    
$form = $this->createForm(ExportType::class, null, []);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$filters = ["status" => $form->get('status')->getData()];
$phpBinaryFinder = new PhpExecutableFinder();
$phpBinaryPath = $phpBinaryFinder->find();
$projectRoot = $this->get('kernel')->getProjectDir();
$process = new Process([$phpBinaryPath, $projectRoot . '/bin/console', 'myapp:files:export', json_encode($filters)]);
$process->setTimeout(36000); //10min
// let the process run in the background
$this->get('event_dispatcher')->addListener(KernelEvents::TERMINATE, function() use($process) {
$process->start();
$process->wait();
});
// render this messsage in twig
$this->addFlash("success", "The export files is initiated");
return $this->redirectToRoute('home');
}
return $this->render('$PATH/export.html.twig', [
'form' => $form->createView()
]);
}

最新更新