Colas de trabajo

Introducción

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.

Configuración

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, IronMQ, Amazon SQS, Redis, and synchronous (for local use) driver.

A null queue driver is also included which simply discards queued jobs.

Driver Prerequisites

Base de datos

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:

php artisan queue:table

php artisan migrate

Other Queue Dependencies

Las siguientes dependencias son necesarias para los controladores de colas de trabajo listados:

  • Amazon SQS: aws/aws-sdk-php ~3.0
  • Beanstalkd: pda/pheanstalk ~3.0
  • IronMQ: iron-io/iron_mq ~2.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:

php artisan make:job SendReminderEmail --queued

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:

<?php

namespace App\Jobs;

use Mail;
use App\User;
use App\Jobs\Job;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Bus\SelfHandling;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendReminderEmail extends Job implements SelfHandling, ShouldQueue
{
    use InteractsWithQueue, SerializesModels;

    protected $user;

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

    /**
     * Execute the job.
     *
     * @param  Mailer  $mailer
     * @return void
     */
    public function handle(Mailer $mailer)
    {
        $mailer->send('emails.reminder', ['user' => $this->user], function ($m) {
            //
        });

        $user->reminders()->create(...);
    }
}

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:

public function handle(Mailer $mailer)
{
    if (condition) {
        $this->release(10);
    }
}

Comprobar el número de intentos de ejecución

As noted above, if an exception occurs while the job is being processed, it will automatically be released back onto the queue. Puedes comprobar el número de intentos de ejecución del trabajo utilizando el método attempts:

public function handle(Mailer $mailer)
{
    if ($this->attempts() > 3) {
        //
    }
}

Pushing Jobs Onto The Queue

The default Laravel controller located in app/Http/Controllers/Controller.php uses a DispatchesJob trait. This trait provides several methods allowing you to conveniently push jobs onto the queue, such as the dispatch method:

<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;
use App\Jobs\SendReminderEmail;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * Send a reminder e-mail to a given user.
     *
     * @param  Request  $request
     * @param  int  $id
     * @return Response
     */
    public function sendReminderEmail(Request $request, $id)
    {
        $user = User::findOrFail($id);

        $this->dispatch(new SendReminderEmail($user));
    }
}

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:

<?php

namespace App;

use Illuminate\Foundation\Bus\DispatchesJobs;

class ExampleClass
{
    use DispatchesJobs;
}

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 base App\Jobs\Job class included with Laravel:

<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;
use App\Jobs\SendReminderEmail;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * Send a reminder e-mail to a given user.
     *
     * @param  Request  $request
     * @param  int  $id
     * @return Response
     */
    public function sendReminderEmail(Request $request, $id)
    {
        $user = User::findOrFail($id);

        $job = (new SendReminderEmail($user))->onQueue('emails');

        $this->dispatch($job);
    }
}

Delayed Jobs

Algunas veces puedes necesitar retrasar la ejecución de un trabajo en cola. For instance, you may wish to queue a job that sends a customer a reminder e-mail 15 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:

<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;
use App\Jobs\SendReminderEmail;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * Send a reminder e-mail to a given user.
     *
     * @param  Request  $request
     * @param  int  $id
     * @return Response
     */
    public function sendReminderEmail(Request $request, $id)
    {
        $user = User::findOrFail($id);

        $job = (new SendReminderEmail($user))->delay(60);

        $this->dispatch($job);
    }
}

In this example, we're specifying that the job should be delayed in the queue for 60 seconds before being made available to workers.

Note: The Amazon SQS service has a maximum delay time of 15 minutes.

Dispatching Jobs From Requests

It is very common to map HTTP request variables into jobs. Por tanto, en lugar de obligarle a hacerlo manualmente en cada petición, Laravel proporciona algunos métodos de ayuda para convertirlo en un juego de niños. Let's take a look at the dispatchFrom method available on the DispatchesJobs trait. By default, this trait is included on the base Laravel controller class:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class CommerceController extends Controller
{
    /**
     * Process the given order.
     *
     * @param  Request  $request
     * @param  int  $id
     * @return Response
     */
    public function processOrder(Request $request, $id)
    {
        // Process the request...

        $this->dispatchFrom('App\Jobs\ProcessOrder', $request);
    }
}

This method will examine the constructor of the given job class and extract variables from the HTTP request (or any other ArrayAccess object) to fill the needed constructor parameters of the job. So, if our job class accepts a productId variable in its constructor, the job bus will attempt to pull the productId parameter from the HTTP request.

También puede pasar un array como tercer argumento del método dispatchFrom. Este array será usado para rellenar cualquier parámetro del constructor que no esté disponible en la petición:

$this->dispatchFrom('App\Jobs\ProcessOrder', $request, [
    'taxPercentage' => 20,
]);

Ejecutar el oyente de cola

Iniciar el oyente de cola

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:

php artisan queue:listen

También puedes especificar que conexión de cola el oyente debe utilizar:

php artisan queue:listen connection

Ten en cuenta que cuando esta tarea ha comenzado, continuará ejecutándose hasta que sea manualmente detenida. Puedes utilizar un monitor de procesos como Supervisor para asegurarte de que el oyente de cola no deja de ejecutarse.

Queue Priorities

You may pass a comma-delimited list of queue connections to the listen job to set queue priorities:

php 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.

Especificar un tiempo de espera para el trabajo

También puedes especificar una longitud de tiempo (en segundos) en que cada trabajo está permitido ejecutarse:

php artisan queue:listen --timeout=60

Especificar intervalo de tiempo antes de obtener nuevos trabajos

Además, se puede especificar el número de segundos de espera antes de obtener nuevos trabajos:

php artisan queue:listen --sleep=5

Ten en cuenta que la cola sólo "dormirá" si no hay trabajos en cola. Si hay más trabajos disponibles, la cola continuará procesándolos sin dormir.

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:

sudo 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:

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3 --daemon
autostart=true
autorestart=true
user=forge
numprocs=8
redirect_stderr=true
stdout_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. Once the configuration file has been created, you may update the Supervisor configuration and start the processes using the following commands:

sudo supervisorctl reread

sudo supervisorctl update

sudo 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:

php artisan queue:work connection --daemon

php artisan queue:work connection --daemon --sleep=3

php artisan queue:work connection --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.

Similarly, your database connection may disconnect when being used by long-running daemon. You may use the DB::reconnect method to ensure you have a fresh connection.

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:

php artisan queue:restart

This command will gracefully instruct all queue workers to restart after they finish processing their current job so that no existing jobs are lost.

Note: 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

Puesto que las cosas no siempre salen según lo planeado, a veces tus trabajos en cola fallarán. No te preocupes, ¡nos pasa a los mejores de nosotros! Laravel incluye una práctica manera de especificar el máximo número de veces que un trabajo debe ser ejecutado. Después que un trabajo ha superado esta cantidad de intentos, se insertará a la tabla failed_jobs. The name of the failed jobs can be configured via the config/queue.php configuration file.

Para crear una migración para la tabla failed_jobs, puedes utilizar el comando queue:failed-table:

php 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:

php 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:

<?php

namespace App\Providers;

use Queue;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Queue::failing(function ($connection, $job, $data) {
            // Notify team of failing job...
        });
    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

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:

<?php

namespace App\Jobs;

use App\Jobs\Job;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Bus\SelfHandling;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendReminderEmail extends Job implements SelfHandling, ShouldQueue
{
    use InteractsWithQueue, SerializesModels;

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

    /**
     * Handle a job failure.
     *
     * @return void
     */
    public function failed()
    {
        // Called when the job is failing...
    }
}

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:

php artisan queue:failed

El comando queue:failed listará el ID del trabajo, conexión, cola, y fecha del fallo. El ID del trabajo puede ser utilizado para reintentar el trabajo fallido. Por ejemplo, para reintentar el trabajo que tiene el ID 5, el siguiente comando debe utilizarse:

php artisan queue:retry 5

Si deseas eliminar un trabajo fallido, puedes utilizar el comando queue:forget:

php artisan queue:forget 5

Para eliminar todos tus trabajos fallidos, puedes utilizar el comando queue:flush:

php artisan queue:flush