Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel Artisan CLI safely stop daemon queue workers

In order to process large numbers of jobs, I run a variable number of queue workers depending on howmuch work there is to complete. I don't want to run more workers than are necessary to complete the work that needs to be done in a time period that we deem appropriate.

At the moment, I start 5 daemon queue workers for testing purposes, however in production this number could be between 25 & 100 workers, possibly more. I understand that when deploying, I have to stop the queue workers by first placing the framework in maintenance mode by using php artisan down, because the --daemon flag causes the framework to only load when the worker starts up, hence new code would not take affect during the deploy until the worker restarts.

If i needed to stop the workers for some reason, I could put the application in maintenance mode using php artisan down which will cause the workers to die once they have finished processing their current job (if they are working one). However, there may be times where I want to kill the workers without putting the whole application in maintenance mode.

Is there a safe way to stop the workers in a way where they will continue processing their current job and then die without placing the whole application in maintenance mode?

Essentially what I need is a php artisan queue:stop, which behaves like php artisan queue:restart, but does not restart the worker once the job is done.

I was expecing there to be a like php artisan queue:stop command that would do this, but that doesn't seem to be the case.

Using ps aux | grep php I am able to get the process id's for the workers, and I could kill the processes that way, but I don't want to kill the process in the middle of it working on a job.

Thanks.

like image 900
Vigs Avatar asked May 05 '15 18:05

Vigs


People also ask

How do I know if my Laravel queue is working?

$this->mailer->queue($view, $data, function ($message) use ($toEmail, $toName, $subject) { $message ->to($toEmail, $toName) ->subject($subject); }); This will successfully run, but if the queue is not 'listening', the job gets pushed on to the job table, forever.

What does php artisan queue listen do?

Using queue:listen ensures that a new instance of the app is created for every job, that means you don't have to manually restart the worker in case you made changes to your code, but also means more server resources will be consumed.


4 Answers

We've implemented something like this in our application - but it was not something that was built-in to Laravel itself. You would have to edit this file, by adding another condition to the if-block so that it would call the stop function. You can do this by either setting a static variable in the Worker class that gets changed whenever you run a custom command that you'll have to make (i.e. php artisan queue:pause) or by checking an atomic value somewhere (i.e. set it in some cache like redis, memcached, APC or even MySQL, though this would mean you'll have one MySQL query for every cycle of this while-loop) that you set using the same custom command.

like image 97
Nikko Avatar answered Oct 23 '22 15:10

Nikko


When using the --daemon flag workers shouldn't quit when the queue is empty.

I think what you are looking for is in the documentation for queues.

The php artisan queue:restart command will prompt the workers to restart after they are done their current job.

like image 43
Deinumite Avatar answered Oct 23 '22 15:10

Deinumite


Since Laravel 5.5 there is an event called Illuminate\Queue\Events\Looping that gets fired from the daemonShouldRun() call inside the main worker loop of Illuminate\Queue\Worker. So if you setup a listener to do your should process jobs check, and return false then the queue worker(s) will stop until the check returns true. There's a sleep between the next time it checks it which you can customise by passing --sleep <seconds> to the queue:work command.

I'm currently using this technique during deployments to stop workers which run inside docker containers, as it's not so easy to run the suggested queue:restart on them without hacking around.

like image 26
alexkb Avatar answered Oct 23 '22 16:10

alexkb


my Laravel is 5.6. you can (kill your pid) don't worry lose your work just load pcntl(extension) Laravel can listen to the signal and safe exit

show part source below(in ./vendor/laravel/framework/src/Illuminate/Queue/Worker.php):

protected function listenForSignals()
{
    pcntl_async_signals(true);

    pcntl_signal(SIGTERM, function () {
        $this->shouldQuit = true;
    });

    pcntl_signal(SIGUSR2, function () {
        $this->paused = true;
    });

    pcntl_signal(SIGCONT, function () {
        $this->paused = false;
    });
}

And my test below:

for($i=0; $i<100; $i++){
        pcntl_async_signals(true);

    pcntl_signal(SIGTERM, function () {
        echo 'SIGTERM';
    });

    pcntl_signal(SIGUSR2, function () {
        echo 'SIGUSR2';
    });

    pcntl_signal(SIGCONT, function () {
        echo 'SIGCONT';
    });
echo $i; 
sleep(1);
 }

you can try to kill it

like image 27
Zuo Zou Avatar answered Oct 23 '22 15:10

Zuo Zou