3

I have a class in my Symfony 2.3 project that is doing some http requests and takes some time.

I would like to run this task as a background process, so that the server returns an answer to the client and the background process continues running.

Do you know how to do that in Symfony?

I found the Process Component: http://symfony.com/doc/current/components/process.html but I am not sure if I can run a class method from there.

luchaninov
  • 6,792
  • 6
  • 60
  • 75
Milos Cuculovic
  • 19,631
  • 51
  • 159
  • 265

2 Answers2

7

A simple way to do this is to separate the heavy lifting from the response by using a queue and a symfony command to process the queue.

http://symfony.com/doc/current/components/console/introduction.html

Create a symfony command that processes the jobs added to a queue, then add the work to be done to the queue from your controller. The queue will probably be implemented as a database table of jobs.

That way you can return a success response to the user and run a cron job on the server regularly to process the work you require.

Jon Winstanley
  • 23,010
  • 22
  • 73
  • 116
  • Thank you Jon, I will try that today. +1 for you. – Milos Cuculovic Nov 25 '13 at 07:58
  • Cron usage for workers is a dangerous thing. Consider this: You run a worker every minute by cron but the worker takes more than a minute to do the job. You ended having a lot of processes competing for resources decreasing the general performance. The server may go down at all. – Maksim Kotlyar Jun 23 '17 at 09:12
0

This is something you could easily do with enqueue library. First, you can choose from a variety of transports, such as AMQP, STOMP, Redis, Amazon SQS, Filesystem and so on.

Secondly, That's super easy to use. Let's start from installation:

You have to install the enqueue/enqueue-bundle library and one of the transports. Assuming you choose the filesystem enqueue/fs library:

composer require enqueue/enqueue-bundle enqueue/fs 

Now let's see how you can send messages from your POST script:

<?php

use Enqueue\Client\ProducerInterface; 
use Symfony\Component\DependencyInjection\Container;

/** @var Container $container */

/** @var ProducerInterface $producer */ $producer = $container->get('enqueue.client.producer');

$producer->sendCommand('a_background_task', 'task_data');

For the consumption, you have to create a processor service and tag it with enqueue.client.processor tag:

<?php

use Enqueue\Client\CommandSubscriberInterface;
use Enqueue\Psr\PsrContext;
use Enqueue\Psr\PsrMessage;
use Enqueue\Psr\PsrProcessor;

class BackgroundTask implements PsrProcessor, CommandSubscriberInterface
{
    public static function getSubscribedCommand()
    {
        // do job

        return self::ACK;
    }

    public function process(PsrMessage $message, PsrContext $context)
    {
        return 'a_background_task';
    }
} 

And run a consumer with a command:

./bin/console enqueue:consume --setup-broker -vvv

On the prod you most likely need more then one consumer and if the process exists it has to be restarted. To address this you need a sort of process manager. There several options:

http://supervisord.org/ - You need extra service. It has to be configured properly. A pure PHP process manager like this. Based on Symfony process component and pure PHP code. It can handle process reboot, correct exit on sigterm signal and a lot more. A php\swoole process manager like this. It requires a swoole PHP extension but it is performance is amazing.

Maksim Kotlyar
  • 3,821
  • 27
  • 31