Queues
- Introduction
- Writing Job Classes
- Pushing Jobs Onto The Queue
- Running The Queue Listener
- Dealing With Failed Jobs
Introduction
The Laravel queue service provides a unified API across a variety of different queue back-ends. Queues allow you to defer the processing of a time consuming task, such as sending an e-mail, until a later time which drastically speeds up web requests to your application.
Configuration
The queue configuration file is stored in config/queue.php. In this file you will find connection configurations for each of the queue drivers that are included with the framework, which includes a database, Beanstalkd, Amazon SQS, Redis, and synchronous (for local use) driver.
A null queue driver is also included which simply discards queued jobs.
Driver Prerequisites
Database
In order to use the database queue driver, you will need a database table to hold the jobs. To generate a migration that creates this table, run the queue:table Artisan command. Once the migration is created, you may migrate your database using the migrate command:
1php artisan queue:table2 3php artisan migrate
Other Queue Dependencies
The following dependencies are needed for the listed queue drivers:
- Amazon SQS:
aws/aws-sdk-php ~3.0 - Beanstalkd:
pda/pheanstalk ~3.0 - Redis:
predis/predis ~1.0
Writing Job Classes
Generating Job Classes
By default, all of the queueable jobs for your application are stored in the app/Jobs directory. You may generate a new queued job using the Artisan CLI:
1php artisan make:job SendReminderEmail
This command will generate a new class in the app/Jobs directory, and the class will implement the Illuminate\Contracts\Queue\ShouldQueue interface, indicating to Laravel that the job should be pushed onto the queue instead of run synchronously.
Job Class Structure
Job classes are very simple, normally containing only a handle method which is called when the job is processed by the queue. To get started, let's take a look at an example job class:
1<?php 2 3namespace App\Jobs; 4 5use App\User; 6use App\Jobs\Job; 7use Illuminate\Contracts\Mail\Mailer; 8use Illuminate\Queue\SerializesModels; 9use Illuminate\Queue\InteractsWithQueue;10use Illuminate\Contracts\Queue\ShouldQueue;11 12class SendReminderEmail extends Job implements ShouldQueue13{14 use InteractsWithQueue, SerializesModels;15 16 protected $user;17 18 /**19 * Create a new job instance.20 *21 * @param User $user22 * @return void23 */24 public function __construct(User $user)25 {26 $this->user = $user;27 }28 29 /**30 * Execute the job.31 *32 * @param Mailer $mailer33 * @return void34 */35 public function handle(Mailer $mailer)36 {37 $mailer->send('emails.reminder', ['user' => $this->user], function ($m) {38 //39 });40 41 $this->user->reminders()->create(...);42 }43}
In this example, note that we were able to pass an Eloquent model directly into the queued job's constructor. Because of the SerializesModels trait that the job is using, Eloquent models will be gracefully serialized and unserialized when the job is processing. If your queued job accepts an Eloquent model in its constructor, only the identifier for the model will be serialized onto the queue. When the job is actually handled, the queue system will automatically re-retrieve the full model instance from the database. It's all totally transparent to your application and prevents issues that can arise from serializing full Eloquent model instances.
The handle method is called when the job is processed by the queue. Note that we are able to type-hint dependencies on the handle method of the job. The Laravel service container automatically injects these dependencies.
When Things Go Wrong
If an exception is thrown while the job is being processed, it will automatically be released back onto the queue so it may be attempted again. The job will continue to be released until it has been attempted the maximum number of times allowed by your application. The number of maximum attempts is defined by the --tries switch used on the queue:listen or queue:work Artisan jobs. More information on running the queue listener can be found below.
Manually Releasing Jobs
If you would like to release the job manually, the InteractsWithQueue trait, which is already included in your generated job class, provides access to the queue job release method. The release method accepts one argument: the number of seconds you wish to wait until the job is made available again:
1public function handle(Mailer $mailer)2{3 if (condition) {4 $this->release(10);5 }6}
Checking The Number Of Run Attempts
As noted above, if an exception occurs while the job is being processed, it will automatically be released back onto the queue. You may check the number of attempts that have been made to run the job using the attempts method:
1public function handle(Mailer $mailer)2{3 if ($this->attempts() > 3) {4 //5 }6}
Pushing Jobs Onto The Queue
The default Laravel controller located in app/Http/Controllers/Controller.php uses a DispatchesJobs trait. This trait provides several methods allowing you to conveniently push jobs onto the queue, such as the dispatch method:
1<?php 2 3namespace App\Http\Controllers; 4 5use App\User; 6use Illuminate\Http\Request; 7use App\Jobs\SendReminderEmail; 8use App\Http\Controllers\Controller; 9 10class UserController extends Controller11{12 /**13 * Send a reminder e-mail to a given user.14 *15 * @param Request $request16 * @param int $id17 * @return Response18 */19 public function sendReminderEmail(Request $request, $id)20 {21 $user = User::findOrFail($id);22 23 $this->dispatch(new SendReminderEmail($user));24 }25}
The DispatchesJobs Trait
Of course, sometimes you may wish to dispatch a job from somewhere in your application besides a route or controller. For that reason, you can include the DispatchesJobs trait on any of the classes in your application to gain access to its various dispatch methods. For example, here is a sample class that uses the trait:
1<?php 2 3namespace App; 4 5use Illuminate\Foundation\Bus\DispatchesJobs; 6 7class ExampleClass 8{ 9 use DispatchesJobs;10}
The dispatch Function
Or, you may use the dispatch global function:
1Route::get('/job', function () {2 dispatch(new App\Jobs\PerformTask);3 4 return 'Done!';5});
Specifying The Queue For A Job
You may also specify the queue a job should be sent to.
By pushing jobs to different queues, you may "categorize" your queued jobs, and even prioritize how many workers you assign to various queues. This does not push jobs to different queue "connections" as defined by your queue configuration file, but only to specific queues within a single connection. To specify the queue, use the onQueue method on the job instance. The onQueue method is provided by the Illuminate\Bus\Queueable trait, which is already included on the App\Jobs\Job base class:
1<?php 2 3namespace App\Http\Controllers; 4 5use App\User; 6use Illuminate\Http\Request; 7use App\Jobs\SendReminderEmail; 8use App\Http\Controllers\Controller; 9 10class UserController extends Controller11{12 /**13 * Send a reminder e-mail to a given user.14 *15 * @param Request $request16 * @param int $id17 * @return Response18 */19 public function sendReminderEmail(Request $request, $id)20 {21 $user = User::findOrFail($id);22 23 $job = (new SendReminderEmail($user))->onQueue('emails');24 25 $this->dispatch($job);26 }27}
The DispatchesJobs trait pushes jobs to queues within the default queue connection.
Specifying The Queue Connection For A Job
If you are working with multiple queue connections, you may specify which connection to push a job to. To specify the connection, use the onConnection method on the job instance. The onConnection method is provided by the Illuminate\Bus\Queueable trait, which is already included on the App\Jobs\Job base class:
1<?php 2 3namespace App\Http\Controllers; 4 5use App\User; 6use Illuminate\Http\Request; 7use App\Jobs\SendReminderEmail; 8use App\Http\Controllers\Controller; 9 10class UserController extends Controller11{12 /**13 * Send a reminder e-mail to a given user.14 *15 * @param Request $request16 * @param int $id17 * @return Response18 */19 public function sendReminderEmail(Request $request, $id)20 {21 $user = User::findOrFail($id);22 23 $job = (new SendReminderEmail($user))->onConnection('alternate');24 25 $this->dispatch($job);26 }27}
Of course, you can also chain the onConnection and onQueue methods to specify the connection and the queue for a job:
1public function sendReminderEmail(Request $request, $id) 2{ 3 $user = User::findOrFail($id); 4 5 $job = (new SendReminderEmail($user)) 6 ->onConnection('alternate') 7 ->onQueue('emails'); 8 9 $this->dispatch($job);10 11}
Delayed Jobs
Sometimes you may wish to delay the execution of a queued job. For instance, you may wish to queue a job that sends a customer a reminder e-mail 5 minutes after sign-up. You may accomplish this using the delay method on your job class, which is provided by the Illuminate\Bus\Queueable trait:
1<?php 2 3namespace App\Http\Controllers; 4 5use App\User; 6use Illuminate\Http\Request; 7use App\Jobs\SendReminderEmail; 8use App\Http\Controllers\Controller; 9 10class UserController extends Controller11{12 /**13 * Send a reminder e-mail to a given user.14 *15 * @param Request $request16 * @param int $id17 * @return Response18 */19 public function sendReminderEmail(Request $request, $id)20 {21 $user = User::findOrFail($id);22 23 $job = (new SendReminderEmail($user))->delay(60 * 5);24 25 $this->dispatch($job);26 }27}
In this example, we're specifying that the job should be delayed in the queue for 5 minutes before being made available to workers.
The Amazon SQS service has a maximum delay time of 15 minutes.
Job Events
Job Lifecycle Events
The Queue::before and Queue::after methods allow you to register a callback to be executed before a queued job is started or when it executes successfully. The callbacks are great opportunity to perform additional logging, queue a subsequent job, or increment statistics for a dashboard. For example, we may attach a callback to this event from the AppServiceProvider that is included with Laravel:
1<?php 2 3namespace App\Providers; 4 5use Queue; 6use Illuminate\Support\ServiceProvider; 7use Illuminate\Queue\Events\JobProcessed; 8 9class AppServiceProvider extends ServiceProvider10{11 /**12 * Bootstrap any application services.13 *14 * @return void15 */16 public function boot()17 {18 Queue::after(function (JobProcessed $event) {19 // $event->connectionName20 // $event->job21 // $event->data22 });23 }24 25 /**26 * Register the service provider.27 *28 * @return void29 */30 public function register()31 {32 //33 }34}
Running The Queue Listener
Starting The Queue Listener
Laravel includes an Artisan command that will run new jobs as they are pushed onto the queue. You may run the listener using the queue:listen command:
1php artisan queue:listen
You may also specify which queue connection the listener should utilize:
1php artisan queue:listen connection-name
Note that once this task has started, it will continue to run until it is manually stopped. You may use a process monitor such as Supervisor to ensure that the queue listener does not stop running.
Queue Priorities
You may pass a comma-delimited list of queue connections to the listen job to set queue priorities:
1php artisan queue:listen --queue=high,low
In this example, jobs on the high queue will always be processed before moving onto jobs from the low queue.
Specifying The Job Timeout Parameter
You may also set the length of time (in seconds) each job should be allowed to run:
1php artisan queue:listen --timeout=60
Specifying Queue Sleep Duration
In addition, you may specify the number of seconds to wait before polling for new jobs:
1php artisan queue:listen --sleep=5
Note that the queue only "sleeps" if no jobs are on the queue. If more jobs are available, the queue will continue to work them without sleeping.
Processing The First Job On The Queue
To process only the first job on the queue, you may use the queue:work command:
1php artisan queue:work
Supervisor Configuration
Supervisor is a process monitor for the Linux operating system, and will automatically restart your queue:listen or queue:work commands if they fail. To install Supervisor on Ubuntu, you may use the following command:
1sudo apt-get install supervisor
Supervisor configuration files are typically stored in the /etc/supervisor/conf.d directory. Within this directory, you may create any number of configuration files that instruct supervisor how your processes should be monitored. For example, let's create a laravel-worker.conf file that starts and monitors a queue:work process:
1[program:laravel-worker]2process_name=%(program_name)s_%(process_num)02d3command=php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3 --daemon4autostart=true5autorestart=true6user=forge7numprocs=88redirect_stderr=true9stdout_logfile=/home/forge/app.com/worker.log
In this example, the numprocs directive will instruct Supervisor to run 8 queue:work processes and monitor all of them, automatically restarting them if they fail. Of course, you should change the queue:work sqs portion of the command directive to reflect your chosen queue driver.
Once the configuration file has been created, you may update the Supervisor configuration and start the processes using the following commands:
1sudo supervisorctl reread2 3sudo supervisorctl update4 5sudo supervisorctl start laravel-worker:*
For more information on configuring and using Supervisor, consult the Supervisor documentation. Alternatively, you may use Laravel Forge to automatically configure and manage your Supervisor configuration from a convenient web interface.
Daemon Queue Listener
The queue:work Artisan command includes a --daemon option for forcing the queue worker to continue processing jobs without ever re-booting the framework. This results in a significant reduction of CPU usage when compared to the queue:listen command:
To start a queue worker in daemon mode, use the --daemon flag:
1php artisan queue:work connection-name --daemon2 3php artisan queue:work connection-name --daemon --sleep=34 5php artisan queue:work connection-name --daemon --sleep=3 --tries=3
As you can see, the queue:work job supports most of the same options available to queue:listen. You may use the php artisan help queue:work job to view all of the available options.
Coding Considerations For Daemon Queue Listeners
Daemon queue workers do not restart the framework before processing each job. Therefore, you should be careful to free any heavy resources before your job finishes. For example, if you are doing image manipulation with the GD library, you should free the memory with imagedestroy when you are done.
Deploying With Daemon Queue Listeners
Since daemon queue workers are long-lived processes, they will not pick up changes in your code without being restarted. So, the simplest way to deploy an application using daemon queue workers is to restart the workers during your deployment script. You may gracefully restart all of the workers by including the following command in your deployment script:
1php artisan queue:restart
This command will gracefully instruct all queue workers to "die" after they finish processing their current job so that no existing jobs are lost. Remember, the queue workers will die when the queue:restart command is executed, so you should be running a process manager such as Supervisor which automatically restarts the queue workers.
This command relies on the cache system to schedule the restart. By default, APCu does not work for CLI jobs. If you are using APCu, add apc.enable_cli=1 to your APCu configuration.
Dealing With Failed Jobs
Since things don't always go as planned, sometimes your queued jobs will fail. Don't worry, it happens to the best of us! Laravel includes a convenient way to specify the maximum number of times a job should be attempted. After a job has exceeded this amount of attempts, it will be inserted into a failed_jobs table. The name of the table can be configured via the config/queue.php configuration file.
To create a migration for the failed_jobs table, you may use the queue:failed-table command:
1php artisan queue:failed-table
When running your queue listener, you may specify the maximum number of times a job should be attempted using the --tries switch on the queue:listen command:
1php artisan queue:listen connection-name --tries=3
Failed Job Events
If you would like to register an event that will be called when a queued job fails, you may use the Queue::failing method. This event is a great opportunity to notify your team via e-mail or HipChat. For example, we may attach a callback to this event from the AppServiceProvider that is included with Laravel:
1<?php 2 3namespace App\Providers; 4 5use Queue; 6use Illuminate\Queue\Events\JobFailed; 7use Illuminate\Support\ServiceProvider; 8 9class AppServiceProvider extends ServiceProvider10{11 /**12 * Bootstrap any application services.13 *14 * @return void15 */16 public function boot()17 {18 Queue::failing(function (JobFailed $event) {19 // $event->connectionName20 // $event->job21 // $event->data22 });23 }24 25 /**26 * Register the service provider.27 *28 * @return void29 */30 public function register()31 {32 //33 }34}
Failed Method On Job Classes
For more granular control, you may define a failed method directly on a queue job class, allowing you to perform job specific actions when a failure occurs:
1<?php 2 3namespace App\Jobs; 4 5use App\Jobs\Job; 6use Illuminate\Contracts\Mail\Mailer; 7use Illuminate\Queue\SerializesModels; 8use Illuminate\Queue\InteractsWithQueue; 9use Illuminate\Contracts\Queue\ShouldQueue;10 11class SendReminderEmail extends Job implements ShouldQueue12{13 use InteractsWithQueue, SerializesModels;14 15 /**16 * Execute the job.17 *18 * @param Mailer $mailer19 * @return void20 */21 public function handle(Mailer $mailer)22 {23 //24 }25 26 /**27 * Handle a job failure.28 *29 * @return void30 */31 public function failed()32 {33 // Called when the job is failing...34 }35}
Retrying Failed Jobs
To view all of your failed jobs that have been inserted into your failed_jobs database table, you may use the queue:failed Artisan command:
1php artisan queue:failed
The queue:failed command will list the job ID, connection, queue, and failure time. The job ID may be used to retry the failed job. For instance, to retry a failed job that has an ID of 5, the following command should be issued:
1php artisan queue:retry 5
To retry all of your failed jobs, use queue:retry with all as the ID:
1php artisan queue:retry all
If you would like to delete a failed job, you may use the queue:forget command:
1php artisan queue:forget 5
To delete all of your failed jobs, you may use the queue:flush command:
1php artisan queue:flush