Aborting and resuming a Symfony Console command

前端 未结 3 1538
無奈伤痛
無奈伤痛 2021-02-02 17:14

I have a Symfony Console command that iterates over a potentially big collection of items and does a task with each of them. Since the collection can be big, the co

相关标签:
3条回答
  • You should take a look at RabbitMqBundle's signal handling. Its execute method just links some callbacks via the pcntl_signal() function call. A common case should look pretty much like this:

    <?php
    use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand as Command;
    
    class YourCommand extends Command
    {
        protected function execute(InputInterface $input, OutputInterface $output)
        {
            pcntl_signal(SIGTERM, array(&$this, 'stopCommand', $output));
            pcntl_signal(SIGINT, array(&$this, 'stopCommand', $output));
            pcntl_signal(SIGHUP, array(&$this, 'restartCommand', $output));
    
            // The real execute method body
        }
    
        public function stopCommand(OutputInterface $output)
        {
            $output->writeln('Stopping');
    
            // Do what you need to stop your process
        }
    
        public function restartCommand(OutputInterface $output)
        {
            $output->writeln('Restarting');
    
            // Do what you need to restart your process
        }
    }
    
    0 讨论(0)
  • 2021-02-02 17:34

    This is what worked for me. You need to call pcntl_signal_dispatch before the signal handlers are actually executed. Without it, all tasks will finish first.

    <?php
    use Symfony\Component\Console\Command\Command;
    
    class YourCommand extends Command
    {
        protected function execute(InputInterface $input, OutputInterface $output)
        {
            pcntl_signal(SIGTERM, [$this, 'stopCommand']);
            pcntl_signal(SIGINT, [$this, 'stopCommand']);
    
            $this->shouldStop = false;
    
            foreach ( $this->tasks as $task )
            {
                pcntl_signal_dispatch();
                if ( $this->shouldStop ) break; 
                $task->execute();
            }
    
            $this->showSomeStats($output);
        }
    
        public function stopCommand()
        {
            $this->shouldStop = true;
        }
    }
    
    0 讨论(0)
  • 2021-02-02 17:40

    The answers are more complex than they need to be. Sure, you can register POSIX signal handlers, but if the only signals that need to be handled are basic interrupts and the like, you should just define a destructor on the Command.

    
    class YourCommand extends Command
    {
        // Other code goes here.
    
        __destruct()
        {
            $this->shouldStop = true;
        }
    }
    

    A case where you would want to register a POSIX signal is for the SIGCONT signal, which can handle the resumption of a process that was stopped (SIGSTOP).

    Another case would be where you want every signal to behave differently; for the most part, though, SIGINT and SIGTERM and a handful of others would be registered with the same "OMG THE PROCESS HAS BEEN KILLED" operation.

    Aside from these examples, registering signal events is unnecessary. This is why destructors exist.

    You can even extend Symfony's base Command class with a __destruct method, which would automatically provide cleanup for every command; should a particular command require additional operations, just overwrite it.

    0 讨论(0)
提交回复
热议问题