-
-
Save ivanvermeyen/b72061c5d70c61e86875 to your computer and use it in GitHub Desktop.
#!/bin/bash | |
# | |
# You can run this bash script with a cron job | |
# or just run the command below. | |
# | |
# I had to use php-cli for both the cron job and the | |
# call to queue:listen in "EnsureQueueListenerIsRunning.php" | |
# to avoid getting the exception: | |
# 'ErrorException' with message 'Invalid argument supplied for foreach()' | |
# in /path/to/vendor/symfony/console/Input/ArgvInput.php:283 | |
# | |
php-cli /path/to/artisan schedule:run >/dev/null 2>&1 |
<?php | |
namespace App\Console\Commands; | |
use Illuminate\Console\Command; | |
class EnsureQueueListenerIsRunning extends Command | |
{ | |
/** | |
* The name and signature of the console command. | |
* | |
* @var string | |
*/ | |
protected $signature = 'queue:checkup'; | |
/** | |
* The console command description. | |
* | |
* @var string | |
*/ | |
protected $description = 'Ensure that the queue listener is running.'; | |
/** | |
* Create a new command instance. | |
*/ | |
public function __construct() | |
{ | |
parent::__construct(); | |
} | |
/** | |
* Execute the console command. | |
* | |
* @return void | |
*/ | |
public function handle() | |
{ | |
if ( ! $this->isQueueListenerRunning()) { | |
$this->comment('Queue listener is being started.'); | |
$pid = $this->startQueueListener(); | |
$this->saveQueueListenerPID($pid); | |
} | |
$this->comment('Queue listener is running.'); | |
} | |
/** | |
* Check if the queue listener is running. | |
* | |
* @return bool | |
*/ | |
private function isQueueListenerRunning() | |
{ | |
if ( ! $pid = $this->getLastQueueListenerPID()) { | |
return false; | |
} | |
$process = exec("ps -p $pid -opid=,cmd="); | |
//$processIsQueueListener = str_contains($process, 'queue:listen'); // 5.1 | |
$processIsQueueListener = ! empty($process); // 5.6 - see comments | |
return $processIsQueueListener; | |
} | |
/** | |
* Get any existing queue listener PID. | |
* | |
* @return bool|string | |
*/ | |
private function getLastQueueListenerPID() | |
{ | |
if ( ! file_exists(__DIR__ . '/queue.pid')) { | |
return false; | |
} | |
return file_get_contents(__DIR__ . '/queue.pid'); | |
} | |
/** | |
* Save the queue listener PID to a file. | |
* | |
* @param $pid | |
* | |
* @return void | |
*/ | |
private function saveQueueListenerPID($pid) | |
{ | |
file_put_contents(__DIR__ . '/queue.pid', $pid); | |
} | |
/** | |
* Start the queue listener. | |
* | |
* @return int | |
*/ | |
private function startQueueListener() | |
{ | |
//$command = 'php-cli ' . base_path() . '/artisan queue:listen --timeout=60 --sleep=5 --tries=3 > /dev/null & echo $!'; // 5.1 | |
$command = 'php-cli ' . base_path() . '/artisan queue:work --timeout=60 --sleep=5 --tries=3 > /dev/null & echo $!'; // 5.6 - see comments | |
$pid = exec($command); | |
return $pid; | |
} | |
} |
<?php | |
namespace App\Console; | |
use App\Console\Commands\EnsureQueueListenerIsRunning; | |
use Illuminate\Console\Scheduling\Schedule; | |
use Illuminate\Foundation\Console\Kernel as ConsoleKernel; | |
class Kernel extends ConsoleKernel | |
{ | |
/** | |
* The Artisan commands provided by your application. | |
* | |
* @var array | |
*/ | |
protected $commands = [ | |
EnsureQueueListenerIsRunning::class | |
]; | |
/** | |
* Define the application's command schedule. | |
* | |
* @param \Illuminate\Console\Scheduling\Schedule $schedule | |
* | |
* @return void | |
*/ | |
protected function schedule(Schedule $schedule) | |
{ | |
$schedule->command('queue:checkup')->everyFiveMinutes(); | |
} | |
} |
I have Laravel 5.6 too and I get an artisan process accumulation even with the changes you proposed @julienmonty
Hi, thanks for reporting. To be honest, I haven't used this anymore in a while now.
I use Laravel Forge to manage the server and start queue workers, which are automatically restarted by supervisord when they die.
I've figured it out. It looks like ps -p $pid -opid=,cmd=
inside a cronjob was truncated and it didn't display the part with the queue:listen
command, so it started a new one each time. I changed str_contains($process, 'queue:listen')
for !empty($process)
.
Thanks, I've updated the gist. 👍
I found a solution to use php
without errors:
instead to use php-cli
use php -d register_argc_argv=On
This kind of feels like reinventing the wheel. Doesn't adding $schedule->command('queue:work')->everyFiveMinutes()->withoutOverlapping(); to kernel.php do the exact same thing?
Will it work for Laravel 8?
Hi, this solution may be outdated, I haven't used it in ages.
I've been using Digital Ocean and Laravel Forge.
In Laravel 8 there are some errors, I found the solution by making these changes.
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
class EnsureQueueListenerIsRunning extends Command
{
protected $signature = 'queue:checkup';
protected $description = 'Ensure that the queue listener is running.';
public function __construct()
{
parent::__construct();
}
public function handle()
{
if ( ! $this->isQueueListenerRunning()) {
$this->comment('Queue listener is being started.');
$pid = $this->startQueueListener();
$this->saveQueueListenerPID($pid);
}
$this->comment('Queue listener is running.');
}
private function isQueueListenerRunning()
{
if ( ! $pid = $this->getLastQueueListenerPID()) {
return false;
}
$process = exec("ps -p {$pid}");
//$processIsQueueListener = str_contains($process, 'queue:listen'); // 5.1
$processIsQueueListener = ! empty($process);
return $processIsQueueListener;
}
private function getLastQueueListenerPID()
{
if ( ! file_exists(__DIR__ . '/queue.pid')) {
return false;
}
return file_get_contents(__DIR__ . '/queue.pid');
}
private function saveQueueListenerPID($pid)
{
file_put_contents(__DIR__ . '/queue.pid', $pid);
}
private function startQueueListener()
{
//$command = 'php-cli ' . base_path() . '/artisan queue:listen --timeout=60 --sleep=5 --tries=3 > /dev/null & echo $!'; // 5.1
//$command = 'php-cli ' . base_path() . '/artisan queue:work --timeout=60 --sleep=5 --tries=3 > /dev/null & echo $!'; // 5.6 - see comments
//handle memory issues
$descriptorspec = [0 => ['pipe', 'r'],1 => ['pipe', 'r'],2 => ['pipe', 'r']];
$command = env('PATH_PHP') . ' ' . base_path() . '\\artisan queue:work --queue=default --delay=0 --once --timeout=600000 --sleep=5 --tries=3 > nul 2>&1 & echo $!';
$proc = proc_open($command, $descriptorspec, $pipes);
$proc_details = proc_get_status($proc);
$pid = $proc_details['pid'];
// $pid = exec($command, $output);
Log::info('t',[$proc_details]);
return $pid;
}
}
This kind of feels like reinventing the wheel. Doesn't adding $schedule->command('queue:work')->everyFiveMinutes()->withoutOverlapping(); to kernel.php do the exact same thing?
or
$schedule->command('queue:work')->everyFiveMinutes()->withoutOverlapping()->runInBackground();
It does not work?
With Laravel 5.6, I encountered a php memory leak (artisan process accumulation). I solved it by replacing
queue:listen
byqueue:work
inisQueueListenerRunning
andstartQueueListener
functions.