w3resource

Laravel (5.7) Laravel Horizon

Introduction

Horizon provides us with a beautiful dashboard and code-driven configuration for our Laravel powered Redis queues. It allows us to easily monitor key metrics of our queue system such as job throughput, runtime, and job failures.

All of our worker configuration is stored in a single, simple configuration file, allowing our configuration to stay in source control where our entire team can collaborate.

Installation

//{note} You need to ensure that your queue driver is set to redis in your queue configuration file.

You can use the Composer to install Horizon into your Laravel project:

composer require laravel/horizon

Once you have installed Horizon, you should publish its assets using the horizon:install Artisan command:

php artisan horizon:install

You have to also create the failed_jobs table which Laravel will use to store any failed queue jobs:

php artisan queue:failed-table
php artisan migrate

Configuration

Once you have published Horizon's assets, you will find its primary configuration file in config/horizon.php. This configuration file will allow you to configure your worker options and each configuration option will include a description of its purpose, so ensure you thoroughly explore this file.

Balance Options

Horizon will allow you to choose from three balancing strategies namely: simple, auto, and false. The simple strategy is the configuration file's default, and it splits incoming jobs evenly between processes:

'balance' => 'simple',

The auto strategy will adjust the number of worker processes per queue based on the current workload of the queue. For instance, if your notifications queue consists of 1,000 waiting jobs while your render queue is empty, Horizon allocates more workers to your notifications queue until it is empty. When you set the balance option to false, the default Laravel behavior is used, this processes queues in the order they are listed in your configuration.

Job Trimming

The horizon configuration file will allow you to configure how long recent and failed jobs should be persisted (in minutes). By default, recent jobs will be kept for one hour while failed jobs are kept for a week:

'trim' => [
    'recent' => 60,
    'failed' => 10080,
],

Dashboard Authorization

Horizon will expose a dashboard at /horizon. By default, you are only able to access this dashboard in the local environment. Inside your app/Providers/HorizonServiceProvider.php file, gate method exists. This authorization gate will control access to Horizon in non-local environments. You are free to modify this gate as you wish, so as to restrict access to your Horizon installation:

/**
 * Register the Horizon gate.
 *
 * This gate will determine who can access Horizon in non-local environments.
 *
 * @return void
 */
protected function gate()
{
    Gate::define('viewHorizon', function ($user) {
        return in_array($user->email, [
            '[email protected]',
        ]);
    });
}

Running Horizon

After you have configured your workers in the config/horizon.php configuration file, you can start Horizon using the horizon Artisan command. This single command starts all of your configured workers:

php artisan horizon

You can pause the Horizon process and instruct it to continue processing jobs by using the horizon:pause and horizon:continue Artisan commands:

php artisan horizon:pause
php artisan horizon:continue

You can gracefully terminate the master Horizon process on your machine using the horizon:terminate Artisan command. Any jobs that Horizon is currently processing is completed and then Horizon exits:

php artisan horizon:terminate

Deploying Horizon

In the case where you are deploying Horizon to a live server, you will need to configure a process monitor to monitor the php artisan horizon command and restart it if it ever quits unexpectedly. When you are deploying fresh code to your server, you need to instruct the master Horizon process to terminate so it can be restarted by your process monitor and receive changes in your code.

Supervisor Configuration

In the case where you are using the Supervisor process monitor to manage your horizon process, the following configuration file should suffice:

[program:horizon]
process_name=%(program_name)s
command=php /home/forge/app.com/artisan horizon
autostart=true
autorestart=true
user=forge
redirect_stderr=true
stdout_logfile=/home/forge/app.com/horizon.log

Tags

Horizon will allow you to assign "tags" to jobs, including mailables, event broadcasts, notifications, and queued event listeners. In fact, Horizon intelligently and automatically tags most jobs depending on the Eloquent models that are attached to the job. For instance, consider the following job:

>?php

namespace App\Jobs;

use App\Video;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class RenderVideo implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * The video instance.
     *
     * @var \App\Video
     */
    public $video;

    /**
     * Create a new job instance.
     *
     * @param  \App\Video  $video
     * @return void
     */
    public function __construct(Video $video)
    {
        $this->video = $video;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        //
    }
}

If this job is queued with an App\Video instance which has an id of 1, it automatically receives sthe tag App\Video:1. This is because Horizon examines the job's properties for any Eloquent models. In the case where Eloquent models are found, Horizon intelligently tags the job using the model's class name and primary key:

$video = App\Video::find(1);
App\Jobs\RenderVideo::dispatch($video);

Manually Tagging

If you want to manually define the tags for one of your queueable objects, you can define a tags method on the class:

class RenderVideo implements ShouldQueue
{
    /**
     * Gets the tags that should be assigned to the job.
     *
     * @return array
     */
    public function tags()
    {
        return [, 'video:'.$this->video->i'render'd];
    }
}

Notifications

//To use notifications, add the guzzlehttp/guzzle Composer package to your project. When you configure Horizon to send SMS notifications, review the prerequisites for the Nexmo notification driver.

If you want to be notified when one of your queues has a long wait time, you can use the Horizon::routeMailNotificationsTo, Horizon::routeSlackNotificationsTo, and Horizon::routeSmsNotificationsTo methods. You can call these methods from your application's HorizonServiceProvider:

Horizon::routeMailNotificationsTo('[email protected]');
Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
Horizon::routeSmsNotificationsTo('15556667777');

Configuring Notification Wait Time Thresholds

You can configure how many seconds are considered a "long wait" within your config/horizon.php configuration file. The waits configuration option within this file will allow you to control the long wait threshold for each connection / queue combination:

'waits' => [
    'redis:default' => 60,
],

Metrics

Horizon has a metrics dashboard which provides information on your job and also queue wait times and throughput. For you to populate this dashboard, configure Horizon's snapshot Artisan command to run every five minutes via your application's scheduler:

/**
 * Defines the application's command schedule.
 *
 * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
 * @return void
 */
protected function schedule(Schedule $schedule)
{
    $schedule->command('horizon:snapshot')->everyFiveMinutes();
}