There are some approaches how to execute queue:work
but I found them useless. Here is a solution for most shared hostings that:
- does not require additional route
- does not require remotely visiting website
- does not require shell access
- does not require cron access
- requires
bash
withflock
on server (for single execution protection) - requires PHP
exec
function available - requires shell
php
command (php-cli
installed on server) - runs in the background so makes no website slowdowns
- can be set to execute not more than once between minimum time span of $runEverySec seconds
1. Let's create new Middleware:
$ php artisan make:middleware QueueWorkMiddleware
2. Use it as global middleware in bootstrap/app.php
:
... ->withMiddleware(function (Middleware $middleware) { $middleware->append(App\Http\Middleware\QueueWorkMiddleware::class); }) ...
3. Put contents to App/Middleware/QueueWorkMiddleware.php
:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; class QueueWorkMiddleware { // lock file that prevents too many executions public $lockFile = 'queue.lock'; // log from queue command public $logFile = 'queue.log'; // log from background exec command public $execLogFile = 'exec.log'; // pid of executed command public $pidFile = 'queue.pid'; // php command path public $phpExec = '/usr/bin/php'; // queue:work command public $queueCmd = 'artisan queue:work --stop-when-empty'; // minimum time in seconds between executions public $runEverySec = 10; /** * Handle an incoming request. * * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ public function handle(Request $request, Closure $next): Response { // get pid and lock file names $pidFile = base_path()."/".$this->pidFile; $lockFile = base_path()."/".$this->lockFile; if( // if there is no lock file and !file_exists($lockFile) && // there is no pid file (queue was never executed before) // or time between pidfile modification is more or equal $runEverySec (!file_exists($pidFile) || (time()-filemtime(base_path()."/{$this->pidFile}") >= $this->runEverySec)) ) { // do the work $this->work(); } return $next($request); } public function work() { // file names $basePath = base_path(); $lockFile = "{$basePath}/{$this->lockFile}"; $logFile = "{$basePath}/{$this->logFile}"; $execLogFile = "{$basePath}/{$this->execLogFile}"; $pidFile = base_path()."/".$this->pidFile; // main queue command and lock file removal $cmd = "{ {$this->phpExec} {$this->queueCmd} > {$logFile} 2>&1; rm {$lockFile}; }"; // go to base path and run command by flock (this guarantees single execution only!) $cmd = "cd {$basePath} && flock -n {$lockFile} --command '{$cmd}'"; // execute command in background exec(sprintf("%s > {$execLogFile} 2>&1 & echo $! >> %s", $cmd, $pidFile)); return true; } }
Top comments (0)