-
-
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:listenbyqueue:workinisQueueListenerRunningandstartQueueListenerfunctions.