-
-
Save mauris/11375869 to your computer and use it in GitHub Desktop.
<?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(); | |
} |
I've adapted this code just slightly and have it running through the Laravel 5.0 scheduling system. I set a cron job to run the artisan schedule:run command every minute per the docs. Inside of app/Console/Kernel.php I put the following in the "schedule" function.:
protected function schedule(Schedule $schedule)
{
$schedule->call(function() {
// check that the queue listener is running and restart it if it isn't
$run_command = false;
if (file_exists(__DIR__ . '/queue.pid')) {
$pid = file_get_contents(__DIR__ . '/queue.pid');
$result = exec("ps -p $pid --no-heading | awk '{print $1}'");
if ($result == '') {
$run_command = true;
}
} else {
$run_command = true;
}
if($run_command)
{
$command = '/usr/local/bin/php /home/user/webapp/artisan queue:listen > /dev/null & echo $!';
$number = exec($command);
file_put_contents(__DIR__ . '/queue.pid', $number);
}
})->name('monitor_queue_listener')->everyFiveMinutes();
}
I tried to get it to work with the symphony process class but got stuck because symphony kills the process when php stops. Have pasted anyway in case anyone would like to see.
use Symfony\Component\Process\Process;
//use Symfony\Component\Process\Exception\ProcessFailedException;
protected function schedule(Schedule $schedule)
{
$schedule->call(function() {
$pidfile = base_path() . DIRECTORY_SEPARATOR . 'queue.pid';
$run_command = false;
if (file_exists($pidfile)) {
$pid = file_get_contents($pidfile);
$command = "ps -p $pid --no-heading | awk '{print $1}'";
$process = new Process($command);
$process->run();
if ($process->isSuccessful()) {
if ($process->getOutput() == '') {
$run_command = true;
}
}
} else {
$run_command = true;
}
if($run_command)
{
$phpBin = $_SERVER['_'];
$artisan = base_path() . DIRECTORY_SEPARATOR . 'artisan';
/* does not work - when php ends, the command ends
$command = $phpBin . ' ' . $artisan . ' queue:listen';
$process = new Process($command);
$process->start();
file_put_contents($pidfile, $process->getPid());
*/
// so let's settle for exec
$command = $phpBin . ' ' . $artisan . ' queue:listen > /dev/null & echo $!';
$number = exec($command);
file_put_contents($pidfile, $number);
}
})->name('monitor_queue_listener')->everyFiveMinutes();
}
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();
}
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);
}
});
$schedule->call(
function(){
// you can pass queue name instead of default
Artisan::call('queue:listen', array('--queue' => 'default'));
}
)->name('ensurequeueisrunning')->withoutOverlapping()->everyMinute();
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();
@di5abled how this function will be called?
or i just have to enqueue my cron jobs and it will run automatically?
Do you have an adaptation of the queue ensurer that will work on a windows server that does not have grep installed?
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);
}
});
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.
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.
Thanks kohlerdominik!,
It's working perfectly.
@kohlerdominik solution work's great! Thanks!
@kohlerdominik perfect solution. Thanks!
What's the purpose of & echo $!
here?
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, runartisan 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"
@alvaro-canepa thanks this work me
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
@CodeAndWeb looks nice, more clean than my code!
Thanks to share
I know this is old, but is this used instead of something likes Supervisor?