Skip to content

Instantly share code, notes, and snippets.

@mauris
Created April 28, 2014 15:44
Show Gist options
  • Save mauris/11375869 to your computer and use it in GitHub Desktop.
Save mauris/11375869 to your computer and use it in GitHub Desktop.
Laravel Artisan Queue Ensurer - Set a cron job to run this file periodically to ensure that Laravel's queue is processing all the time. If the queue listener stopped, restart it!
<?php
function runCommand ()
{
$command = 'php artisan queue:listen > /dev/null & echo $!';
$number = exec($command);
file_put_contents(__DIR__ . '/queue.pid', $number);
}
if (file_exists(__DIR__ . '/queue.pid')) {
$pid = file_get_contents(__DIR__ . '/queue.pid');
$result = exec('ps | grep ' . $pid);
if ($result == '') {
runCommand();
}
} else {
runCommand();
}
@xxzefgh
Copy link

xxzefgh commented Apr 26, 2016

Confirmed working on 5.2

<?php

function runCommand ()
{
    $command = 'php ' . __DIR__ . '/artisan queue:listen > /dev/null & echo $!';
    $number = exec($command);
    file_put_contents(__DIR__ . '/queue.pid', $number);
}

if (file_exists(__DIR__ . '/queue.pid')) {
    $pid = file_get_contents(__DIR__ . '/queue.pid');
    $result = exec('ps -e | grep ' . $pid);
    if ($result == '') {
        runCommand();
    }
} else {
    runCommand();
}

@xxzefgh
Copy link

xxzefgh commented Apr 26, 2016

Also if anyone wants to check if artisan is running with ajax

Route::get('queue_status', function() {
    $output = function ($success) {
        return response()->json(['running' => $success]);
    };

    $pidFile = base_path() . '/queue.pid';
    if (file_exists($pidFile)) {
        $pid = file_get_contents($pidFile);
        $result = exec('ps -e | grep ' . $pid);
        if ($result == '') {
            return $output(false);
        } else {
            return $output(true);
        }
    } else {
        return $output(false);
    }
});

@mohamedsharaf
Copy link

mohamedsharaf commented May 4, 2016

 $schedule->call(
            function(){
                // you can pass queue name instead of default
                Artisan::call('queue:listen', array('--queue' => 'default'));
            }
        )->name('ensurequeueisrunning')->withoutOverlapping()->everyMinute();

@di5abled
Copy link

di5abled commented Jul 5, 2016

Improved the code a little bit and it seems to work fine for me in my current L 5.2:

$schedule->call(function() {
    $run_command = false;
    $monitor_file_path = storage_path('queue.pid');

    if (file_exists($monitor_file_path)) {
        $pid = file_get_contents($monitor_file_path);
        $result = exec("ps -p $pid --no-heading | awk '{print $1}'");

        if ($result == '') {
            $run_command = true;
        }
    } else {
        $run_command = true;
    }

    if($run_command)
    {
        $command = 'php '. base_path('artisan'). ' queue:listen > /dev/null & echo $!';
        $number = exec($command);
        file_put_contents($monitor_file_path, $number);
    }
})
->name('monitor_queue_listener')
->everyFiveMinutes();

Copy link

ghost commented Jul 14, 2016

@di5abled how this function will be called?

Copy link

ghost commented Jul 14, 2016

or i just have to enqueue my cron jobs and it will run automatically?

@ratatatKE
Copy link

Do you have an adaptation of the queue ensurer that will work on a windows server that does not have grep installed?

@peyman1992
Copy link

peyman1992 commented Dec 10, 2016

if exec function is disable you can use this code

        $schedule->call(function () {
            $descriptorspec = array(
                0 => array("pipe", "r"),
                1 => array("pipe", "w"),
            );
            $runCommand = false;
            $queueFile = storage_path('queue.pid');
            if (file_exists($queueFile)) {
                $pid = intval(file_get_contents($queueFile));
                $process = proc_open("ps -p $pid --no-heading | awk '{print $1}'", $descriptorspec, $pipes);
                $result = '';
                if (is_resource($process)) {
                    $result = stream_get_contents($pipes[1]);
                    fclose($pipes[1]);
                }

                if ($result == '') {
                    $runCommand = true;
                }
            } else {
                $runCommand = true;
            }
            if ($runCommand) {
                $dir = base_path() . DIRECTORY_SEPARATOR;
                $command = "php {$dir}artisan queue:listen --timeout=60 --tries=1 > '/dev/null' 2>&1 & echo $!";
                $process = proc_open("$command", $descriptorspec, $pipes, null, ["register_argc_argv" => "on"]);
                $number = '';
                if (is_resource($process)) {
                    $number = stream_get_contents($pipes[1]);
                    fclose($pipes[1]);
                }
                file_put_contents($queueFile, $number);
            }
        });

@weotch
Copy link

weotch commented Feb 22, 2017

This sounds like a good solution:

// Using `queue:work` for Laravel 5.4
$schedule->command('queue:work')->everyMinute()->withoutOverlapping();

Anyone see any issue with it?


Nvm, it doesn't background the queue:work like this snippet does.

@kohlerdominik
Copy link

kohlerdominik commented Aug 24, 2017

A made a simple solution for this (Laravel 5.5 dev).

I created a new function, that checks if the process is running.
Then, a simple if-condition arround the command does the thing

        // start the queue daemon, if its not running
        if ( !$this->osProcessIsRunning('queue:work') ) {
            $schedule->command('queue:work')  ->everyMinute();
        }

I put the new function right in app/Console/Commands/Kernel.php:

    /**
     * checks, if a process with $needle in the name is running
     *
     * @param string $needle
     * @return bool
     */
    protected function osProcessIsRunning($needle)
    {   
        // get process status. the "-ww"-option is important to get the full output!
        exec('ps aux -ww', $process_status);

        
        // search $needle in process status
        $result = array_filter($process_status,
            function($var) use ($needle)
            {   return strpos($var, $needle); });


        // if the result is not empty, the needle exists in running processes
        if (!empty($result)) {
            
            return true;
        }

        return false;
    }

Edit: Execution time on my dev-machine is 3-4ms.

@developmentvk
Copy link

Thanks kohlerdominik!,
It's working perfectly.

@maurodaprotis
Copy link

@kohlerdominik solution work's great! Thanks!

@henryonsoftware
Copy link

@kohlerdominik perfect solution. Thanks!

@ArchTaqi
Copy link

What's the purpose of & echo $! here?

@alvaro-canepa
Copy link

For shared hosted with disabled exec functions, I made a shell script.

Cron job:
* * * * * cd /path/to/public_html && /usr/bin/sh artisan.call.sh >> /dev/null 2>&1

How work

  • Check for folder /storage/queue.lock/
  • If not exists, call artisan queue:work and create the folder, otherwise do nothing.
  • Check for file /storage/.queue-restart, if exists, run artisan queue:restart and remove folder /storage/queue.lock/

When run artisan queue:restart the script must exit and auto remove queue.lock folder, next time call artisan queue:work.

#!/bin/bash

FULL_PATH_TO_SCRIPT="$(realpath "$0")"
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
LOCKFILE="$SCRIPT_DIRECTORY/storage/queue.lock"
LOGFILE="$SCRIPT_DIRECTORY/storage/logs/queue.log"
QUEUE_RESTART_FILE="$SCRIPT_DIRECTORY/storage/logs/.queue-restart"

d=$(date '+%F %T')

log()
{
    echo '=============================================================================================' >> "${LOGFILE}"
    echo "$d" >> "${LOGFILE}"

    if [ "$1" ]; then
      echo "$1" >> "${LOGFILE}"
    fi
}

clean_log()
{
    truncate -s 0 "${LOGFILE}"
}

remove_lock()
{
    log "Removing lock file $LOCKFILE… exiting"
    rmdir "$LOCKFILE"
}

another_instance()
{
    log "Lock file $LOCKFILE exists… exiting"
    exit 1
}

run_artisan()
{
      if [ "$1" ]; then
        (php ${SCRIPT_DIRECTORY}/artisan $1)
      fi
}

if ! mkdir "${LOCKFILE}" 2>/dev/null; then
    another_instance
    exit 1
fi

if [ -f "${QUEUE_RESTART_FILE}" ]; then
    rm "$QUEUE_RESTART_FILE"
    log "restarting queue"
    run_artisan "queue:restart"
    remove_lock
    exit 1
fi

trap remove_lock QUIT INT TERM EXIT
clean_log
log "runing php \"${SCRIPT_DIRECTORY}/artisan queue:work"\"
run_artisan "queue:work"

@henkiws
Copy link

henkiws commented Feb 21, 2023

@alvaro-canepa thanks this work me

@CodeAndWeb
Copy link

I also need the shell-script variant... pgrep allows to use it without the pid file.
However you can only run one instance of php artisan queue:work

I use pgrep to only search for the artisan queue:work since my provider uses an alias for php.

#!/bin/bash

# Check if "artisan queue:work" is running
if pgrep -f "artisan queue:work" > /dev/null
then
    echo "queue:work is already running."
    exit 1
else
    echo "Starting queue:work..."
    nohup php artisan queue:work > /dev/null 2>&1 &
    echo "queue:work started."
fi

@alvaro-canepa
Copy link

@CodeAndWeb looks nice, more clean than my code!

Thanks to share

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment