In this chapter we will cover how to set up Redis in-memory data store and Laravel Horizon to provide a beautiful dashboard and code-driven configuration for Redis queues.
Redis is often preferred over a database for implementing queues because of its speed. In-memory key-value store makes it very fast compared to MySQL. In a queue scenario, where data needs to be accessed and processed quickly, Redis can handle requests much faster than MySQL.
Horizon allows us to easily monitor key metrics of the queue system such as job throughput, runtime, and job failures.
The advantage of using Horizon is that all queue worker configuration is stored in a single, simple configuration file and that allows us easily scale or modify queue workers when deploying the application. For example, in the previous chapter, we set the number of workers in the supervisor configuration.
To use Redis with Laravel we need the PHP driver for redis.
One option is to install the predis/predis ~1.0
package via composer:
composer require predis/predis:~1.0
Or install a system-wide PHP extension.
In this course, we chose to install the Redis PHP Extension.
sudo apt-get install redis php8.1-redis
sudo systemctl restart php8.1-fpm.service
sudo dnf install redis php-pecl-redis5.x86_64
sudo systemctl restart php-fpm.service
Then we can install Horizon using the composer:
composer require laravel/horizon
After installing Horizon, publish its assets using the horizon:install
command:
php artisan horizon:install
And update queue connection in your environment file:
.env
QUEUE_CONNECTION=redis
Horizon queue worker can be launched using this command:
php artisan horizon
Now we can try again by registering a new user and the verification email should be delivered.
We can set up Supervisor for Horizon in the same fashion as we did for queue workers.
For supervisor configuration locations please refer to Chapter 2.
Config content should look as follows:
[program:laravel-horizon]directory=/home/web/laravel-queuescommand=php artisan horizon process_name=%(program_name)s_%(process_num)02dautostart=trueautorestart=trueuser=webnumprocs=1redirect_stderr=truestdout_logfile=/home/web/.supervisor/laravel-horizon.logstopwaitsecs=3600
Important to note that the
numprocs
value is set to1
. Worker processes are managed by Horizon and the configuration file is located inconfig/horizon.php
in the Laravel project directory.
When defining your Supervisor configuration, you should ensure that the value of
stopwaitsecs
is greater than the number of seconds consumed by your longest-running job. Otherwise, Supervisor may kill the job before it is finished processing.
You may gracefully terminate the Horizon process using the horizon:terminate
command. Any jobs that are currently being processed will be completed and then Horizon will stop executing:
php artisan horizon:terminate
This command should be included in your deployment script.
The default configuration of Horizon right after installation looks like this:
config/horizon.php
use Illuminate\Support\Str; return [ // ... 'domain' => env('HORIZON_DOMAIN'), // ... 'path' => env('HORIZON_PATH', 'horizon'), // ... 'use' => 'default', // ... 'prefix' => env( 'HORIZON_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:' ), // ... 'middleware' => ['web'], // ... 'waits' => [ 'redis:default' => 60, ], // ... 'trim' => [ 'recent' => 60, 'pending' => 60, 'completed' => 60, 'recent_failed' => 10080, 'failed' => 10080, 'monitored' => 10080, ], // ... 'silenced' => [ // App\Jobs\ExampleJob::class, ], // ... 'metrics' => [ 'trim_snapshots' => [ 'job' => 24, 'queue' => 24, ], ], // ... 'fast_termination' => false, // ... 'memory_limit' => 64, // ... 'defaults' => [ 'supervisor-1' => [ 'connection' => 'redis', 'queue' => ['default'], 'balance' => 'auto', 'autoScalingStrategy' => 'time', 'maxProcesses' => 1, 'maxTime' => 0, 'maxJobs' => 0, 'memory' => 128, 'tries' => 1, 'timeout' => 60, 'nice' => 0, ], ], 'environments' => [ 'production' => [ 'supervisor-1' => [ 'maxProcesses' => 10, 'balanceMaxShift' => 1, 'balanceCooldown' => 3, ], ], 'local' => [ 'supervisor-1' => [ 'maxProcesses' => 3, ], ], ],];
After installation, the primary Horizon configuration option that you should familiarize yourself with is the environments
configuration option. This configuration option is an array of environments that your application runs on and defines the worker process options for each environment.
Typically, the environment is determined by the value of the APP_ENV
environment variable.
Horizon exposes a dashboard at the /horizon
URL.
Let's quickly run through the dashboard, we will come back to it later.
Here you will be presented with an overview and statistics about your queue workers like how many jobs were run in the past hour, how busy are queues, or how long jobs are waiting in queue to start being processed.
The pending Jobs tab will show all jobs in the queue waiting for their turn.
The completed Jobs tab has insights into how long it took for it to complete.
By default, you will only be able to access this dashboard in the local
environment. However, within your app/Providers/HorizonServiceProvider.php
file, there is an authorization gate definition. This authorization gate controls access to Horizon in non-local environments.
We can update the gate()
method to include user emails which could be able to access the dashboard in production or use any other logic.
app/Providers/HorizonServiceProvider.php
protected function gate(): void{ Gate::define('viewHorizon', function ($user) { return in_array($user->email, [ 'admin-email@example.org' ]); });}