Verification: a143cc29221c9be0

Php artisan queue work в фоне

Introduction

Laravel queues provide a unified API across a variety of different queue backends, such as Beanstalk, Amazon SQS, Redis, or even a relational database. Queues allow you to defer the processing of a time consuming task, such as sending an email, until a later time. Deferring these time consuming tasks drastically speeds up web requests to your application.

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 a synchronous driver that will execute jobs immediately (for local use). A null queue driver is also included which simply discards queued jobs.

Connections Vs. Queues

Before getting started with Laravel queues, it is important to understand the distinction between "connections" and "queues". In your config/queue.php configuration file, there is a connections configuration option. This option defines a particular connection to a backend service such as Amazon SQS, Beanstalk, or Redis. However, any given queue connection may have multiple "queues" which may be thought of as different stacks or piles of queued jobs.

Note that each connection configuration example in the queue configuration file contains a queue attribute. This is the default queue that jobs will be dispatched to when they are sent to a given connection. In other words, if you dispatch a job without explicitly defining which queue it should be dispatched to, the job will be placed on the queue that is defined in the queue attribute of the connection configuration:


dispatch(new Job);


dispatch((new Job)->onQueue('emails'));

Some applications may not need to ever push jobs onto multiple queues, instead preferring to have one simple queue. However, pushing jobs to multiple queues can be especially useful for applications that wish to prioritize or segment how jobs are processed, since the Laravel queue worker allows you to specify which queues it should process by priority. For example, if you push jobs to a high queue, you may run a worker that gives them higher processing priority:

php artisan queue:work --queue=high,default

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 has been created, you may migrate your database using the migrate command:

php artisan queue:table

php artisan migrate

Redis

In order to use the redis queue driver, you should configure a Redis database connection in your config/database.php configuration file.

If your Redis queue connection uses a Redis Cluster, your queue names must contain a key hash tag. This is required in order to ensure all of the Redis keys for a given queue are placed into the same hash slot:

'redis' => [
    'driver' => 'redis',
    'connection' => 'default',
    'queue' => '{default}',
    'retry_after' => 90,
],

Other Driver Prerequisites

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

Creating Jobs

Generating Job Classes

By default, all of the queueable jobs for your application are stored in the app/Jobs directory. If the app/Jobs directory doesn't exist, it will be created when you run the make:job Artisan command. You may generate a new queued job using the Artisan CLI:

php artisan make:job SendReminderEmail

The generated class will implement the Illuminate\Contracts\Queue\ShouldQueue interface, indicating to Laravel that the job should be pushed onto the queue to run asynchronously.

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. In this example, we'll pretend we manage a podcast publishing service and need to process the uploaded podcast files before they are published:

namespace App\Jobs;

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

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

    protected $podcast;

    
    public function __construct(Podcast $podcast)
    {
        $this->podcast = $podcast;
    }

    
    public function handle(AudioProcessor $processor)
    {
        
    }
}

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.

Binary data, such as raw image contents, should be passed through the base64_encode function before being passed to a queued job. Otherwise, the job may not properly serialize to JSON when being placed on the queue.

Dispatching Jobs

Once you have written your job class, you may dispatch it using the dispatch helper. The only argument you need to pass to the dispatch helper is an instance of the job:

namespace App\Http\Controllers;

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

class PodcastController extends Controller
{
    
    public function store(Request $request)
    {
        

        dispatch(new ProcessPodcast($podcast));
    }
}
The dispatch helper provides the convenience of a short, globally available function, while also being extremely easy to test. Check out the Laravel testing documentation to learn more.

Delayed Dispatching

If you would like to delay the execution of a queued job, you may use the delay method on your job instance. The delay method is provided by the Illuminate\Bus\Queueable trait, which is included by default on all generated job classes. For example, let's specify that a job should not be available for processing until 10 minutes after it has been dispatched:

namespace App\Http\Controllers;

use Carbon\Carbon;
use App\Jobs\ProcessPodcast;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PodcastController extends Controller
{
    
    public function store(Request $request)
    {
        

        $job = (new ProcessPodcast($podcast))
                    ->delay(Carbon::now()->addMinutes(10));

        dispatch($job);
    }
}
The Amazon SQS queue service has a maximum delay time of 15 minutes.

Customizing The Queue & Connection

Dispatching To A Particular Queue

By pushing jobs to different queues, you may "categorize" your queued jobs and even prioritize how many workers you assign to various queues. Keep in mind, 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:

namespace App\Http\Controllers;

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

class PodcastController extends Controller
{
    
    public function store(Request $request)
    {
        

        $job = (new ProcessPodcast($podcast))->onQueue('processing');

        dispatch($job);
    }
}

Dispatching To A Particular Connection

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:

namespace App\Http\Controllers;

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

class PodcastController extends Controller
{
    
    public function store(Request $request)
    {
        

        $job = (new ProcessPodcast($podcast))->onConnection('sqs');

        dispatch($job);
    }
}

Of course, you may chain the onConnection and onQueue methods to specify the connection and the queue for a job:

$job = (new ProcessPodcast($podcast))
                ->onConnection('sqs')
                ->onQueue('processing');

Specifying Max Job Attempts / Timeout Values

Max Attempts

One approach to specifying the maximum number of times a job may be attempted is via the --tries switch on the Artisan command line:

php artisan queue:work --tries=3

However, you may take a more granular approach by defining the maximum number of attempts on the job class itself. If the maximum number of attempts is specified on the job, it will take precedence over the value provided on the command line:

namespace App\Jobs;

class ProcessPodcast implements ShouldQueue
{
    
    public $tries = 5;
}

Timeout

The timeout feature is optimized for PHP 7.1+ and the pcntl PHP extension.

Likewise, the maximum number of seconds that jobs can run may be specified using the --timeout switch on the Artisan command line:

php artisan queue:work --timeout=30

However, you may also define the maximum number of seconds a job should be allowed to run on the job class itself. If the timeout is specified on the job, it will take precedence over any timeout specified on the command line:

namespace App\Jobs;

class ProcessPodcast implements ShouldQueue
{
    
    public $timeout = 120;
}

Error Handling

If an exception is thrown while the job is being processed, the job 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 maximum number of attempts is defined by the --tries switch used on the queue:work Artisan command. Alternatively, the maximum number of attempts may be defined on the job class itself. More information on running the queue worker can be found below.

Running The Queue Worker

Laravel includes a queue worker that will process new jobs as they are pushed onto the queue. You may run the worker using the queue:work Artisan command. Note that once the queue:work command has started, it will continue to run until it is manually stopped or you close your terminal:

php artisan queue:work
To keep the queue:work process running permanently in the background, you should use a process monitor such as Supervisor to ensure that the queue worker does not stop running.

Remember, queue workers are long-lived processes and store the booted application state in memory. As a result, they will not notice changes in your code base after they have been started. So, during your deployment process, be sure to restart your queue workers.

Processing A Single Job

The --once option may be used to instruct the worker to only process a single job from the queue:

php artisan queue:work --once

Specifying The Connection & Queue

You may also specify which queue connection the worker should utilize. The connection name passed to the work command should correspond to one of the connections defined in your config/queue.php configuration file:

php artisan queue:work redis

You may customize your queue worker even further by only processing particular queues for a given connection. For example, if all of your emails are processed in an emails queue on your redis queue connection, you may issue the following command to start a worker that only processes only that queue:

php artisan queue:work redis --queue=emails

Resource Considerations

Daemon queue workers do not "reboot" the framework before processing each job. Therefore, you should free any heavy resources after each job completes. For example, if you are doing image manipulation with the GD library, you should free the memory with imagedestroy when you are done.

Queue Priorities

Sometimes you may wish to prioritize how your queues are processed. For example, in your config/queue.php you may set the default queue for your redis connection to low. However, occasionally you may wish to push a job to a high priority queue like so:

dispatch((new Job)->onQueue('high'));

To start a worker that verifies that all of the high queue jobs are processed before continuing to any jobs on the low queue, pass a comma-delimited list of queue names to the work command:

php artisan queue:work --queue=high,low

Queue Workers & Deployment

Since queue workers are long-lived processes, they will not pick up changes to your code without being restarted. So, the simplest way to deploy an application using queue workers is to restart the workers during your deployment process. You may gracefully restart all of the workers by issuing the queue:restart command:

php artisan queue:restart

This command will instruct all queue workers to gracefully "die" after they finish processing their current job so that no existing jobs are lost. Since the queue workers will die when the queue:restart command is executed, you should be running a process manager such as Supervisor to automatically restart the queue workers.

The queue uses the cache to store restart signals, so you should verify a cache driver is properly configured for your application before using this feature.

Job Expirations & Timeouts

Job Expiration

In your config/queue.php configuration file, each queue connection defines a retry_after option. This option specifies how many seconds the queue connection should wait before retrying a job that is being processed. For example, if the value of retry_after is set to 90, the job will be released back onto the queue if it has been processing for 90 seconds without being deleted. Typically, you should set the retry_after value to the maximum number of seconds your jobs should reasonably take to complete processing.

The only queue connection which does not contain a retry_after value is Amazon SQS. SQS will retry the job based on the Default Visibility Timeout which is managed within the AWS console.

Worker Timeouts

The queue:work Artisan command exposes a --timeout option. The --timeout option specifies how long the Laravel queue master process will wait before killing off a child queue worker that is processing a job. Sometimes a child queue process can become "frozen" for various reasons, such as an external HTTP call that is not responding. The --timeout option removes frozen processes that have exceeded that specified time limit:

php artisan queue:work --timeout=60

The retry_after configuration option and the --timeout CLI option are different, but work together to ensure that jobs are not lost and that jobs are only successfully processed once.

The --timeout value should always be at least several seconds shorter than your retry_after configuration value. This will ensure that a worker processing a given job is always killed before the job is retried. If your --timeout option is longer than your retry_after configuration value, your jobs may be processed twice.

Worker Sleep Duration

When jobs are available on the queue, the worker will keep processing jobs with no delay in between them. However, the sleep option determines how long the worker will "sleep" if there are no new jobs available. While sleeping, the worker will not process any new jobs - the jobs will be processed after the worker wakes up again.

php artisan queue:work --sleep=3

Supervisor Configuration

Installing Supervisor

Supervisor is a process monitor for the Linux operating system, and will automatically restart your queue:work process if it fails. To install Supervisor on Ubuntu, you may use the following command:

sudo apt-get install supervisor
If configuring Supervisor yourself sounds overwhelming, consider using Laravel Forge, which will automatically install and configure Supervisor for your Laravel projects.

Configuring 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
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. Of course, you should change the queue:work sqs portion of the command directive to reflect your desired queue connection.

Starting Supervisor

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 Supervisor, consult the Supervisor documentation.

Dealing With Failed Jobs

Sometimes your queued jobs will fail. Don't worry, things don't always go as planned! 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 the failed_jobs database table. To create a migration for the failed_jobs table, you may use the queue:failed-table command:

php artisan queue:failed-table

php artisan migrate

Then, when running your queue worker, you should specify the maximum number of times a job should be attempted using the --tries switch on the queue:work command. If you do not specify a value for the --tries option, jobs will be attempted indefinitely:

php artisan queue:work redis --tries=3

Cleaning Up After Failed Jobs

You may define a failed method directly on your job class, allowing you to perform job specific clean-up when a failure occurs. This is the perfect location to send an alert to your users or revert any actions performed by the job. The Exception that caused the job to fail will be passed to the failed method:

namespace App\Jobs;

use Exception;
use App\Podcast;
use App\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class ProcessPodcast implements ShouldQueue
{
    use InteractsWithQueue, Queueable, SerializesModels;

    protected $podcast;

    
    public function __construct(Podcast $podcast)
    {
        $this->podcast = $podcast;
    }

    
    public function handle(AudioProcessor $processor)
    {
        
    }

    
    public function failed(Exception $exception)
    {
        
    }
}

Failed Job Events

If you would like to register an event that will be called when a job fails, you may use the Queue::failing method. This event is a great opportunity to notify your team via email or HipChat. For example, we may attach a callback to this event from the AppServiceProvider that is included with Laravel:

namespace App\Providers;

use Illuminate\Support\Facades\Queue;
use Illuminate\Queue\Events\JobFailed;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    
    public function boot()
    {
        Queue::failing(function (JobFailed $event) {
            
            
            
        });
    }

    
    public function register()
    {
        
    }
}

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

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, issue the following command:

php artisan queue:retry 5

To retry all of your failed jobs, execute the queue:retry command and pass all as the ID:

php artisan queue:retry all

If you would like to delete a failed job, you may use the queue:forget command:

php artisan queue:forget 5

To delete all of your failed jobs, you may use the queue:flush command:

php artisan queue:flush

Введение

Компонент Laravel Queue предоставляет единое API для различных сервисов очередей. Очереди позволяют вам отложить выполнение времязатратной задачи, такой как отправка e-mail, на более позднее время, таким образом на порядок ускоряя обработку запросов в вашем приложении.

Настройки очередей хранятся в файле config/queue.php. В нём вы найдёте настройки для каждого драйвера очереди, которые поставляются вместе с фреймворком: база данных, Beanstalkd, Amazon SQS, Redis, а также синхронный драйвер (для локального использования). Драйвер очереди null просто отменяет задачи очереди, поэтому они никогда не выполнятся.

Подключения против Очередей

Прежде чем начать работать с очередями Laravel, важно понимать различия между "подключениями" и "очередями". В вашем конфиге config/queue.php есть опция настройки connections. Она определяет определенное полдключение к бэкенд-сервису, такому как Amazon SQS, Beanstalk или Redis. Однако, у любого заданного подключения очереди есть несколько "очередей", о которых можно думать как о различных стеках или пачках задач, стоящих в в очереди.

Обратите внимание, что каждый пример настройки подключения в конфиге queue содержит атрибут queue. Это очередь, в которую по умолчанию буду попадать задачи, когда они отправляются на заданное подключение. Другими словами, если вы отправите задачу без четкого указания того, в какую очередь её следует послать, эта задача будет помещена в ту очередь, которая определена атрибутом queue настройки подключения:


dispatch(new Job);


dispatch((new Job)->onQueue('emails'));

Некоторым приложениям может даже не требоваться помещать задачи в несколько очередей; вместо этого им будет предпочтительно иметь одну простую очередь. Однако, помещать задачи в несколько очередей может быть особенно полезно для приложений, которые хотят приоретизировать или сегментировать то, как обрабатываются задачи, так как воркер очередей Laravel позволяет указывать приоритетность очередей. Например, если вы поместите задачу в очередь high, вы можете запустить воркера, который присвоит ей более высокий приоритет обработки:

php artisan queue:work --queue=high,default

Требования для драйверов

База данных

Для использования драйвера очереди database вам понадобится таблица в БД для хранения задач. Чтобы генерировать миграцию для создания этой таблицы, выполните Artisan-команду queue:table. Когда миграция создана, вы можете мигрировать свою базу данных командой migrate:

php artisan queue:table

php artisan migrate

Redis

Для использования драйвера очереди redis вам потребуется настроить подключение к БД Redis в конфиге config/database.php.

Если ваше Redis-подключение использует Redis Cluster, название вашей очередт должно содержать ключевой хэштег (key hash tag). Это обязательно, чтобы убедиться, что все ключи Redis для заданной очереди помещены в один и тот же хэш-слот:

'redis' => [
    'driver' => 'redis',
    'connection' => 'default',
    'queue' => '{default}',
    'retry_after' => 90,
],

Требования других драйверов

Упомянутым выше драйверам нужны следующие зависимости:

  • Amazon SQS: aws/aws-sdk-php ~3.0
  • Beanstalkd: pda/pheanstalk ~3.0
  • Redis: predis/predis ~1.0

Создание задач

Генерирование классов задач

По умолчанию все помещаемые в очередь задачи вашего приложения хранятся в директории app/Jobs. Вы можете сгенерировать новую задачу для очереди с помощью Artisan-команды make:job. Новую задачу в очереди можно сгенерировать используя Artisan CLI:

php artisan make:job SendReminderEmail

Сгенерированный класс будет реализацией интерфейса Illuminate\Contracts\Queue\ShouldQueue - так Laravel поймёт, что задачу надо поместить в очередь, а не выполнить немедленно.

Структура класса

Классы задач очень просты, обычно они содержат только метод handle, который вызывается при обработке задачи в очереди. Для начала давайте посмотрим на пример класса задачи. В этом примере мы представим, что управляем сервисом публикации подкастов и нам нужно обработать загруженные файлы подкастов прежде, чем они будут опубликованы:

namespace App\Jobs;

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

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

    protected $podcast;

    
    public function __construct(Podcast $podcast)
    {
        $this->podcast = $podcast;
    }

    
    public function handle(AudioProcessor $processor)
    {
        
    }
}

Обратите внимание, в этом примере мы можем передать модель Eloquent напрямую в конструктор задачи. Благодаря используемому в задаче трейту SerializesModels, модели Eloquent будут изящно сериализованы и десериализованы при выполнении задачи. Если ваша задача принимает модель Eloquent в своём конструкторе, в очередь будет сериализован только идентификатор модели. А когда очередь начнёт обработку задачи, система очередей автоматически запросит полный экземпляр модели из БД. Это всё полностью прозрачно для вашего приложения и помогает избежать проблем, связанных с сериализацией полных экземпляров моделей Eloquent.

Метод handle вызывается при обработке задачи очередью. Обратите внимание, что мы можем указывать зависимости в методе handle задачи. Сервис-контейнер Laravel автоматически внедрит эти зависимости.

Двоичные данные, такие как raw-содержимое изображений, нужно передавать через функцию base64_encode перед передачей в задачу в очереди. Иначе задача может некорректно сериализироваться в JSON во время помещения в очередь.

Добавление задач в очередь

Как только вы написали класс своей очереди, его можно направить используя хелпер dispatch. Единственный аргумент, который следует передавать хелперу dispatch - экземпляр задачи:

namespace App\Http\Controllers;

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

class PodcastController extends Controller
{
    
    public function store(Request $request)
    {
        

        dispatch(new ProcessPodcast($podcast));
    }
}
Хелпер dispatch предоставляет удобство короткой, глобально доступной функции, в то же время являясь очень простым в тестировании. Ознакомьтесь с документацией о тестировании Laravel, чтобы узнать об этом побольше.

Отложенные задачи

Если вам нужно задержать выполнение задачи в очереди, то можно использовать метод delay на экземпляре своей задачи. Метод delay предоставляется трейтом Illuminate\Bus\Queueable, который по умолчанию включен на всех генерируемых классах задач. Например, давайте укажем, что задаче не следует быть доступной для обработки до истечения 10 минут после ее отправки:

namespace App\Http\Controllers;

use Carbon\Carbon;
use App\Jobs\ProcessPodcast;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PodcastController extends Controller
{
    
    public function store(Request $request)
    {
        

        $job = (new ProcessPodcast($podcast))
                    ->delay(Carbon::now()->addMinutes(10));

        dispatch($job);
    }
}
У сервиса задач Amazon SQS максимальное время загрузки составляет 15 минут.

Настройка очереди и подключения

Задание очереди для задачи

Помещая задачи в разные очереди, вы можете разделять их по категориям, а также задавать приоритеты по количеству обработчиков разных очередей. Это не касается различных «подключений» очередей, определённых в файле настроек очереди, а только конкретных очередей в рамках одного подключения. Чтобы указать очередь используйте метод onQueue на экземпляре задачи:

namespace App\Http\Controllers;

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

class PodcastController extends Controller
{
    
    public function store(Request $request)
    {
        

        $job = (new ProcessPodcast($podcast))->onQueue('processing');

        dispatch($job);
    }
}

Указание подключения к очереди для задачи

Если вы работаете с несколькими подключениями к очередям, то можете указать, в какое из них надо поместить задачу. Для этого служит метод onConnection на экземпляре задачи:

namespace App\Http\Controllers;

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

class PodcastController extends Controller
{
    
    public function store(Request $request)
    {
        

        $job = (new ProcessPodcast($podcast))->onConnection('sqs');

        dispatch($job);
    }
}

Само собой, вы можете сцепить методы onConnection и onQueue, чтобы указать подключение и очередь для задачи:

$job = (new ProcessPodcast($podcast))
                ->onConnection('sqs')
                ->onQueue('processing');

Указание макс. попыток задач / значений таймаута

Максимальное число попыток

Один из подходов к определению максимального количества попыток задания может быть выполнен с помощью оператора выбора --tries в командной строке Artisan:

php artisan queue:work --tries=3

Однако, можно использовать более детализированный подход, указав максимальное число попыток в самом классе задачи. Если для задачи указано максимальное число попыток, это будет иметь приоритет над значением, указанным в командной строке:

namespace App\Jobs;

class ProcessPodcast implements ShouldQueue
{
    
    public $tries = 5;
}

Таймаут

Функция timeout fоптимизирована для PHP 7.1+ и PHP-расширения pcntl.

Аналогично, максимальное количество секунд, которые может быть запущена задача, можно указать посредством параметра --timeout в командной строке Artisan:

php artisan queue:work --timeout=30

Вы также можете самостоятельно задать максимальное количество секунд, на протяжении которых может выполняться задача. Если для задачи указан таймаут, это будет иметь приоритет над таймаутом, указанным в командной строке:

namespace App\Jobs;

class ProcessPodcast implements ShouldQueue
{
    
    public $timeout = 120;
}

Обработка ошибок

Если во время обработки задачи выбрасывается исключение, задача будет автоматически помещена обратно в очередь, чтобы ее можно было бы попытаться выполнить снова. Задача будет продолжать попытки выполнения, пока не достигнет установленного в вашем приложении максимального количества попыток. Максимальное количество попыток определено в параметре --tries, используемом в Artisan-команде queue:work. В качестве альтернативы, максимальное количество попыток можно задать в самом классе задачи. Больше информации о воркере очереди можно найти ниже.

Выполнение воркера очереди

В Laravel включен воркер очереди, который будет обрабатывать новые задачи по мере их помещения в очередь. Воркер можно запустить при помощи Artisan-команды queue:work. Обратите внимание, что как только была запущена команда queue:work, она продолжит выполняться до тех пор, пока ее не остановить вручную, или пока вы не закроете терминал:

php artisan queue:work
Чтобы процесс queue:work постоянно работал в фоне, нужно использовать инструмент мониторинга процессов, такой как Supervisor, чтобы убедиться, что ворке очереди не перестал работать.

Помните, что воркеры очереди - длительные процессы и хранят в памяти состояние загруженного приложения. В результате, они не заметят изменений в вашей базе кода после своего запуска. Поэтому во время процесса развертывания убедитесь, что перезапустили воркеров очереди.

Обработка одной задачи

Опцию --once можно использовать, чтобы указать воркеру обработать только одну задачу из очереди:

php artisan queue:work --once

Указание подключения и очереди

Можно указать какое подключение к очереди должен использовать воркер. Название подключения передается команде work - оно должен соответствовать одному из подключений, заданных в вашем конфиге config/queue.php:

php artisan queue:work redis

Можно настроить ваш воркер очереди и далее просто обработыв определенные очереди для заданного подключения. Например, если все ваши email-сообщения обрабатываются в очереди emails вашего подключения очереди redis, вы можете использовать следующую команду, чтобы запустить воркер, который обрабатыват только эту очередь:

php artisan queue:work redis --queue=emails

Рекомендации по ресурсам

Демоны-воркеры очереди не "перезагружают" фреймворк перед обработкой каждой задачи. Таким образом, вам нужно освободить ресурсы после выполнения каждой задачи. К примеру, если вы совершаете манипуляции с изображениями при помощи библиотеки GD, вам нужно освободить память при помощи imagedestroy после того как вы закончили.

Приоритеты очереди

Иногда вам может потребоваться приоретизировать то, как обрабатываются ваши очереди. Например, вconfig/queue.php можно задать queueпо умолчанию для вашего подключения redis равным low. Однако, время от времени вы можете захотеть присвоить очереди высокий приоритет следующим образом:

dispatch((new Job)->onQueue('high'));

Для запуска вворкера, который проверяет, что все high-задачи очереди обрабатываются до перехода к любым low-задачам в этой же очереди, передайте разделенный запятой список названий очередей команде work:

php artisan queue:work --queue=high,low

Воркеры очереди и развертывание

Так как воркеры очереди - длительные процессы и хранят в памяти состояние загруженного приложения. В результате, они не заметят изменений в вашей базе кода после своего запуска. Поэтому самый простой способ развернуть приложения используя воркеры очереди - перезагрузить воркеров во время процесса развертывания. Это можно сделать командой queue:restart:

php artisan queue:restart

Данная команда поручит всем воркерам корректно завершить работу после того как они завершат обработку своей текущей задачи, чтобы не потерять ни одну из существующих задач. Так как воркеры прекратят свою работу после выполнения команды queue:restart, у вас должен быть запущен менеджер процессов, такой как Supervisor. Он поможет автоматически перезагрузить воркеров очереди.

Очередь использует кэш для хранения сигналов перезапуска, поэтому следует убедиться в правильной настройке драйвера кэша для вашего приложения прежде, чем использовать этот функционал.

Истечение срока задачи и Таймауты

Истечение срока задачи

В вашем конфиге config/queue.php каждое подключение очереди определяет опцию retry_after. Данная опция указывает сколько секунд должно ждать подключение очереди перед повторным выполнением задачи, которая сейчас обрабатывается. Например, если значение retry_after равно 90, задача будет выпущена обратно в очередь, если она обрабатывалась на протяжении 90 секунд без удаления. Обычно вам нужно будет устанавливать значение retry_after равным максимальному количеству секунд, на протяжении которых ваща задача должна успеть выполнить, что полагается.

Есдинственное подключение очереди, у которого нет значения retry_after - Amazon SQS. SQS заново попробует выполнить задачу основываясь на Таймауте видимости по умолчанию (Default Visibility Timeout), которым можно управлять из консоли AWS.

Таймауты воркера

Artisan-команда queue:work раскрывает опцию --timeout. Опция --timeout указывает как долго мастер-процессу очереди Laravel нужно ждать перед завершением дочернего воркера очереди, который обрабатывает задачу. Иногда дочерний процесс очереди может быть "заморожен" по различным причинам, таким как внешняя HTTP-функция, которая не отвечает. Опция --timeout убирает замороженные процессы, которые превзошли этот указанный лимит времени:

php artisan queue:work --timeout=60

Опция настройки retry_after и CLI-опция --timeout различаются, но работают вместе, чтобы убедиться в том, что задачи не потеряны и что задачи успешно обрабатываются только один раз.

Значение --timeout всегда должно быть равным как минимум на несколько секунд короче, чем ваше значение retry_after. Таким образом, воркер, обрабатывающий определенную задачу, всегда уничтожается перед повторной попыткой выполнить задачу. Если ваша опция --timeout дольше, чем значение retry_after, ваши задачи обрабатываться дважды.

Продолжительность сна воркера

Когда задачи доступны в очереди воркер будет продолжать обрабатывать задачи без задержки между ними. Однако, опция sleep определяет как долго будет "спать" воркер, если нет новых доступных задач. Во время сна воркер не будет обрабатывать никакие новые задачи - все они будут обработаны после повторного пробуждения воркера.

php artisan queue:work --sleep=3

Настройка Supervisor

Установка Supervisor

Supervisor — монитор процессов для ОС Linux, он автоматически перезапустит ваш процесс queue:work, если он остановится. Для установки Supervisor в Ubuntu можно использовать такую команду:

sudo apt-get install supervisor
Если самостоятельно настроить Supervisor вам сложно, попробуйте использовать Laravel Forge, который автоматически установит и настроит Supervisor для ваших Laravel-проектов.

Настройка Supervisor

Файлы настроек Supervisor обычно находятся в директории /etc/supervisor/conf.d. Там вы можете создать любое количество файлов с настройками, по которым Supervisor поймёт, как отслеживать ваши процессы. Например, давайте создадим файл laravel-worker.conf, который запускает и наблюдает за процессом queue:work:

[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
autostart=true
autorestart=true
user=forge
numprocs=8
redirect_stderr=true
stdout_logfile=/home/forge/app.com/worker.log

В этом примере директива numprocs указывает, что Supervisor должен запустить 8 процессов queue:work и наблюдать за ними, автоматически перезапуская их при их остановках. Само собой, вам надо изменить часть queue:work sqs директивы command в соответствии с вашим драйвером очереди.

Запуск Supervisor

После создания файла настроек вы можете обновить конфигурацию Supervisor и запустить процесс при помощи следующих команд:

sudo supervisorctl reread

sudo supervisorctl update

sudo supervisorctl start laravel-worker:*

Подробнее о настройке и использовании Supervisor читайте в документации Supervisor.

Проваленные задачи

Не всегда всё идёт по плану, иногда ваши задачи в очереди будут заканчиваться ошибкой. Не волнуйтесь, такое с каждым случается! В Laravel есть удобный способ указать максимальное количество попыток выполнения задачи. После превышения этого количества попыток задача будет добавлена в таблицу failed_jobs. Для создании миграции таблицы failed_jobs можно использовать команду queue:failed-table:

php artisan queue:failed-table

php artisan migrate

Затем,, во время запуска вашего воркера очереди, вы должны указать максимальное количество попыток выполнить задачу, используя оператор выбора --tries команды queue:work. Если вы не укажете значение для настройки --tries, попытки выполнения задачи будут неограничены:

php artisan queue:work redis --tries=3

Очистка после проваленных задач

Вы можете задать метод failed напрямую в своем классе задач, что позволит вам выполнить очистку специально после этой задачи в случае провала. Это отличное место для отправки уведомления вашим пользователям или для отката любых действий, выполненных задачей. То исключение Exception, которое способствовало провалу задачи, будет передано методу failed:

namespace App\Jobs;

use Exception;
use App\Podcast;
use App\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class ProcessPodcast implements ShouldQueue
{
    use InteractsWithQueue, Queueable, SerializesModels;

    protected $podcast;

    
    public function __construct(Podcast $podcast)
    {
        $this->podcast = $podcast;
    }

    
    public function handle(AudioProcessor $processor)
    {
        
    }

    
    public function failed(Exception $exception)
    {
        
    }
}

События проваленных задач

Если вы хотите зарегистрировать событие, которое будет вызываться при ошибке выполнения задачи, можете использовать метод Queue::failing. Это событие — отличная возможность оповестить вашу команду через e-mail или HipChat. Например, мы можем прикрепить анонимную функцию к данному событию из AppServiceProvider, который включён в Laravel:

namespace App\Providers;

use Illuminate\Support\Facades\Queue;
use Illuminate\Queue\Events\JobFailed;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    
    public function boot()
    {
        Queue::failing(function (JobFailed $event) {
            
            
            
        });
    }

    
    public function register()
    {
        
    }
}

Повторный запуск проваленных задач

Чтобы просмотреть все проваленные задачи, которые были помещены в вашу таблицу failed_jobs, можно использовать Artisan-команду queue:failed:

php artisan queue:failed

Эта команда выведет список задач с их ID, подключением, очередью и временем ошибки. ID задачи можно использовать для повторной попытки её выполнения. Например, для повторной попытки выполнения задачи с ID= 5 надо выполнить такую команду:

php artisan queue:retry 5

Чтобы повторить все проваленные задачи, используйте queue:retry с указанием all в качестве ID:

php artisan queue:retry all

Если вы хотите удалить проваленную задачу, используйте команду queue:forget:

php artisan queue:forget 5

Для удаления всех проваленных задач используйте команду queue:flush:

php artisan queue:flush

Введение

Очереди Laravel предоставляют единое API для различных сервисов очередей, таких как Beanstalk, Amazon SQS, Redis или даже реляционных БД. Очереди позволяют вам отложить выполнение времязатратных задач, таких как отправка e-mail, на более позднее время, таким образом на порядок ускоряя обработку веб-запросов в вашем приложении.

Настройки очередей хранятся в файле config/queue.php. В нём вы найдёте настройки для каждого драйвера очереди, которые поставляются вместе с фреймворком: база данных, Beanstalkd, IronMQ (только для версии 5.1 и ранее), Amazon SQS, Redis, null, а также синхронный драйвер (для локального использования). Драйвер null просто отменяет задачи очереди, поэтому они никогда не выполнятся.

+ 5.3

добавлено в 5.3 ()

Подключения или очереди

Перед изучением очередей Laravel важно понимать различие между «подключениями» и «очередями». В вашем файле настроек config/queue.php есть параметр connections, который определяет конкретное подключение к сервису очередей, такому как Amazon SQS, Beanstalk или Redis. Но у любого подключения может быть несколько «очередей», которые можно представить в виде отдельных стеков или наборов задач в очереди.

Обратите внимание, в каждом примере настройки подключения в файле queue есть атрибут queue. Это та очередь, в которую будут помещаться задачи при отправке по данному подключению. Другими словами, если вы отправляете задачу без явного указания очереди, задача будет помещена в очередь, заданную в атрибуте queue в настройках подключения:

PHP

// Эта задача отправлена в очередь по умолчанию...
dispatch(new Job);// Эта задача отправлена в очередь "emails"...
dispatch((new Job)->onQueue('emails'));

Для некоторых приложений нет необходимости помещать задачи в несколько очередей, для них достаточно иметь одну простую очередь. Иметь несколько очередей особенно полезно для приложений, в которых необходимо разделение обрабатываемых задач по сегментам или приоритетам. Обработчик очереди Laravel позволяет вам указывать какие задачи имеют больший приоритет. Например, если вы отправите задачу в очередь high, то можете запустить обработчик, который даст им больший приоритет при выполнении:

shphp artisan queue:work --queue=high,default

Требования для драйверов

База данных

Для использования драйвера очереди database вам понадобится таблица в БД для хранения задач. Чтобы генерировать миграцию для создания этой таблицы, выполните Artisan-команду shqueue:table. Когда миграция будет создана, вы сможете мигрировать свою базу данных командой shmigrate:

shphp artisan queue:table

php artisan migrate

+ 5.3

добавлено в 5.3 ()

Redis

Для использования драйвера очереди redis вам надо настроить подключение к базе данных Redis в файле config/database.php.

Если ваше подключение к очереди Redis использует Redis Cluster, имена ваших очередей должны содержать хештег ключа. Это необходимо, чтобы убедиться в том, что все ключи Redis для данной очереди помещены в один хеш-слот:

conf'redis' => [
  'driver' => 'redis',
  'connection' => 'default',
  'queue' => '{default}',
  'retry_after' => 90,
],

Требования других очередей

Упомянутые выше драйвера имеют следующие зависимости:

  • Amazon SQS: aws/aws-sdk-php ~3.0
  • Beanstalkd: pda/pheanstalk ~3.0
  • IronMQ: iron-io/iron_mq ~2.0|~4.0 (только для версии 5.1 и ранее)
  • Redis: predis/predis ~1.0

Создание задач

Генерирование классов задач

По умолчанию все помещаемые в очередь задачи вашего приложения хранятся в папке app/Jobs (в версии 5.0 App\Commands). Если папки app/Jobs не существует, она будет создана при запуске Artisan-команды shmake:job. Вы можете сгенерировать новую задачу для очереди с помощью Artisan-команды:

+ 5.1

добавлено в 5.1 ()

shphp artisan make:job SendReminderEmail --queued

+ 5.0

добавлено в 5.0 ()

shphp artisan make:command SendEmail --queued

Сгенерированный класс будет реализацией интерфейса Illuminate\Contracts\Queue\ShouldQueue, так Laravel поймёт, что задачу надо поместить в очередь, а не выполнить немедленно.

Структура класса

Классы задач очень просты, обычно они содержат только метод PHPhandle(), который вызывается при обработке задачи в очереди. Для начала давайте посмотрим на пример класса задачи.

+ 5.3

добавлено в 5.3 ()

В этом примере мы представим, что создаём сервис публикации подкастов, и нам надо обрабатывать загружаемые файлы подкастов перед их публикацией:

PHP

namespace App\Jobs;

use 

App\Podcast;
use 
App\AudioProcessor;
use 
Illuminate\Bus\Queueable;
use 
Illuminate\Queue\SerializesModels;
use 
Illuminate\Queue\InteractsWithQueue;
use 
Illuminate\Contracts\Queue\ShouldQueue;

class 

ProcessPodcast implements ShouldQueue
{
  use 
InteractsWithQueueQueueableSerializesModels;

  protected 

$podcast;/**
   * Создание нового экземпляра задачи.
   *
   * @param  Podcast  $podcast
   * @return void
   */
  
public function __construct(Podcast $podcast)
  {
    
$this->podcast $podcast;
  }
/**
   * Выполнение задачи.
   *
   * @param  AudioProcessor  $processor
   * @return void
   */
  
public function handle(AudioProcessor $processor)
  {
    
// Обработка загруженного подкаста...
  
}
}

+ 5.2 5.1 5.0

добавлено в 5.2 () 5.1 () 5.0 ()

PHP

namespace App\Jobs;

use 

App\User;
use 
App\Jobs\Job;
use 
Illuminate\Contracts\Mail\Mailer;
use 
Illuminate\Queue\SerializesModels;
use 
Illuminate\Queue\InteractsWithQueue;
use 
Illuminate\Contracts\Queue\ShouldQueue;//для версии 5.1 и ранее:
//use Illuminate\Contracts\Bus\SelfHandling;
//
//class SendReminderEmail extends Job implements SelfHandling, ShouldQueue
class SendReminderEmail extends Job implements ShouldQueue
{
  use 
InteractsWithQueueSerializesModels;

  protected 

$user;/**
   * Создание нового экземпляра задачи.
   *
   * @param  User  $user
   * @return void
   */
  
public function __construct(User $user)
  {
    
$this->user $user;
  }
/**
   * Выполнение задачи.
   *
   * @param  Mailer  $mailer
   * @return void
   */
  
public function handle(Mailer $mailer)
  {
    
$mailer->send('emails.reminder', ['user' => $this->user], function ($m) {
      
//
    
});$this->user->reminders()->create(...);
  }
}

Обратите внимание, в этом примере мы можем передать модель Eloquent напрямую в конструктор задачи. Благодаря используемому в задаче типажу SerializesModels, модели Eloquent будут изящно сериализованы и десериализованы при выполнении задачи. Если ваша задача принимает модель Eloquent в своём конструкторе, в очередь будет сериализован только идентификатор модели. А когда очередь начнёт обработку задачи, система очередей автоматически запросит полный экземпляр модели из БД. Это всё полностью прозрачно для вашего приложения и помогает избежать проблем, связанных с сериализацией полных экземпляров моделей Eloquent.

Метод PHPhandle() вызывается при обработке задачи очередью. Обратите внимание, что мы можем указывать зависимости в методе PHPhandle() задачи. сервис-контейнер Laravel автоматически внедрит эти зависимости.

+ 5.0

добавлено в 5.0 ()

Если вы хотите, чтобы у команды был отдельный класс-обработчик, вам надо добавить ключ sh--handler в команду shmake:command:

shphp artisan make:command SendEmail --queued --handler

+ 5.3

добавлено в 5.3 ()

Бинарные данные, такие как сырое содержимое изображения, должны быть пропущены через функцию PHPbase64_encode() перед отправкой в задачу для очереди. Иначе задача может быть неправильно сериализована в JSON при помещении в очередь.

+ 5.2 5.1 5.0

добавлено в 5.2 () 5.1 () 5.0 ()

Ручной выпуск задач

Если вы хотите «отпустить» задачу вручную, то типаж InteractsWithQueue, который уже включён в ваш сгенерированный класс задачи, предоставляет доступ к методу задачи PHPrelease(). Этот метод принимает один аргумент — число секунд, через которое задача станет снова доступной:

PHP

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

Проверка числа попыток запуска

Как уже было сказано, при возникновении исключения при выполнении задачи она будет автоматически возвращена в очередь. Вы можете проверить число сделанных попыток выполнения задачи при помощи метода PHPattempts():

PHP

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

Отправка задач в очередь

+ 5.3

добавлено в 5.3 ()

Когда вы напишете класс вашей задачи, вы можете отправить её в очередь с помощью вспомогательного метода PHPdispatch(). Необходимо передать единственный аргумент — экземпляр задачи:

PHP

namespace App\Http\Controllers;

use 

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

class 

PodcastController extends Controller
{
  
/**
   * Сохранить новый подкаст.
   *
   * @param  Request  $request
   * @return Response
   */
  
public function store(Request $request)
  {
    
// Создать подкаст...dispatch(new ProcessPodcast($podcast));
  }
}

Вспомогательный метод PHPdispatch() — удобная, лаконичная, доступная глобально функция, а также её чрезвычайно просто тестировать. Узнайте больше из документации по тестированию.

+ 5.2 5.1 5.0

добавлено в 5.2 () 5.1 () 5.0 ()

Стандартный контроллер Laravel расположен в app/Http/Controllers/Controller.php и использует типаж DispatchesJobs. Этот типаж предоставляет несколько методов, позволяющих вам удобно помещать задачи в очередь. Например, метод PHPdispatch():

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
{
  
/**
   * Послать уведомление по e-mail данному пользователю.
   *
   * @param  Request  $request
   * @param  int  $id
   * @return Response
   */
  
public function sendReminderEmail(Request $request$id)
  {
    
$user User::findOrFail($id);$this->dispatch(new SendReminderEmail($user));
  }
}

+ 5.2

добавлено в 5.2 ()

Типаж DispatchesJobs

Разумеется, иногда нужно отправить задачу из другого места вашего приложения — не из маршрута или контроллера. Для этого вы можете включить типаж DispatchesJobs в любой класс своего приложения, чтобы получить доступ к его различным методам отправки. Например, вот пример класса, использующего этот типаж:

PHP

namespace App;

use 

Illuminate\Foundation\Bus\DispatchesJobs;

class 

ExampleClass
{
  use 
DispatchesJobs;
}

Функция PHPdispatch()

Или, вы можете использовать глобальную функцию PHPdispatch():

PHP

Route::get('/job', function () {
  
dispatch(new App\Jobs\PerformTask);

  return 

'Готово!';
});

+ 5.0

добавлено в 5.0 ()

Для добавления новой задачи в очередь используйте метод PHPQueue::push():

PHP

Queue::push(new SendEmail($message));

В этом примере мы используем фасад Queue напрямую. Но обычно вы будете отправлять команды в очередь через командную шину. В этой статье мы продолжим использовать фасад Queue, но также коснёмся и командной шины, поскольку она используется и для отправки синхронных задач, и для отправки задач в очередь.

Сгенерированный обработчик будет помещён в App\Handlers\Commands и будет извлечён из IoC-контейнера.

Отложенные задачи

Если вам надо отложить выполнение задачи в очереди, используйте метод PHPdelay() на экземпляре задачи. Этот метод обеспечивается типажом Illuminate\Bus\Queueable, который включён во все генерируемые классы задач по умолчанию.

+ 5.3

добавлено в 5.3 ()

Например, давайте укажем, что задача станет доступной для обработки через 10 минут после отправки в очередь:

PHP

namespace App\Http\Controllers;

use 

Carbon\Carbon;
use 
App\Jobs\ProcessPodcast;
use 
Illuminate\Http\Request;
use 
App\Http\Controllers\Controller;

class 

PodcastController extends Controller
{
  
/**
   * Сохранить новый подкаст.
   *
   * @param  Request  $request
   * @return Response
   */
  
public function store(Request $request)
  {
    
// Создать подкаст...$job = (new ProcessPodcast($podcast))
                ->
delay(Carbon::now()->addMinutes(10));dispatch($job);
  }
}

+ 5.2 5.1 5.0

добавлено в 5.2 () 5.1 () 5.0 ()

Например, вы захотите поместить в очередь задачу, которая отправляет пользователю e-mail через 5 минут после регистрации:

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
{
  
/**
   * Отправить уведомление на e-mail данному пользователю.
   *
   * @param  Request  $request
   * @param  int  $id
   * @return Response
   */
  
public function sendReminderEmail(Request $request$id)
  {
    
$user User::findOrFail($id);$job = (new SendReminderEmail($user))->delay(60 5);$this->dispatch($job);
  }
}

В этом примере мы указали, что задача в очереди должна быть отложена на 5 минут до того, как станет доступной обработчикам.

Сервис Amazon SQS имеет ограничение на задержку не более 15 минут.

+ 5.0

добавлено в 5.0 ()

Вы можете добиться этого, используя метод PHPQueue::later():

PHP

$date Carbon::now()->addMinutes(15);Queue::later($date, new SendEmail($message));

В этом примере мы используем библиотеку для работы со временем Carbon, чтобы указать необходимое время задержки для задачи. Другой вариант — передать необходимое число секунд для задержки.

Настройка очереди и подключения

Задание очереди для задачи

+ 5.0

добавлено в 5.0 ()

Вы можете задать очередь, в которую следует посылать задачу:

PHP

Queue::pushOn('emails', new SendEmail($message));

Помещая задачи в разные очереди, вы можете разделять их по категориям, а также задавать приоритеты по количеству обработчиков разных очередей. Это не касается различных «подключений» очередей, определённых в файле настроек очереди, а только конкретных очередей в рамках одного подключения. Чтобы указать очередь используйте метод PHPonQueue() на экземпляре задачи:

+ 5.3

добавлено в 5.3 ()

PHP

namespace App\Http\Controllers;

use 

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

class 

PodcastController extends Controller
{
  
/**
   * Сохранить новый подкаст.
   *
   * @param  Request  $request
   * @return Response
   */
  
public function store(Request $request)
  {
    
// Создать подкаст...$job = (new ProcessPodcast($podcast))->onQueue('processing');dispatch($job);
  }
}

+ 5.2 5.1 5.0

добавлено в 5.2 () 5.1 () 5.0 ()

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
{
  
/**
   * Отправить уведомление на e-mail данному пользователю.
   *
   * @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);
  }
}

+ 5.0

добавлено в 5.0 ()

Передача одинаковых данных в несколько задач

Если вам надо передать одинаковые данные в несколько задач в очереди, вы можете использовать метод PHPQueue::bulk():

PHP

Queue::bulk([new SendEmail($message), new AnotherCommand]);

+ 5.3 5.2

добавлено в 5.3 () 5.2 ()

Указание подключения к очереди для задачи

Если вы работаете с несколькими подключениями к очередям, то можете указать, в какое из них надо поместить задачу. Для этого служит метод PHPonConnection() на экземпляре задачи:

+ 5.3

добавлено в 5.3 ()

PHP

namespace App\Http\Controllers;

use 

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

class 

PodcastController extends Controller
{
  
/**
   * Сохранить новый подкаст.
   *
   * @param  Request  $request
   * @return Response
   */
  
public function store(Request $request)
  {
    
// Создать подкаст...$job = (new ProcessPodcast($podcast))->onConnection('sqs');dispatch($job);
  }
}

+ 5.2

добавлено в 5.2 ()

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
{
  
/**
   * Послать данному пользователю напоминание на e-mail.
   *
   * @param  Request  $request
   * @param  int  $id
   * @return Response
   */
  
public function sendReminderEmail(Request $request$id)
  {
    
$user User::findOrFail($id);$job = (new SendReminderEmail($user))->onConnection('alternate');$this->dispatch($job);
  }
}

+ 5.3 5.2

добавлено в 5.3 () 5.2 ()

Само собой, вы можете сцепить методы PHPonConnection() и PHPonQueue(), чтобы указать подключение и очередь для задачи:

PHP

$job = (new ProcessPodcast($podcast))
                ->
onConnection('sqs')
                ->
onQueue('processing');

Обработка ошибок

Если во время выполнения задачи возникло исключение, она будет автоматически возвращена в очередь, и можно будет снова попробовать выполнить её. Попытки будут повторяться до тех пор, пока не будет достигнуто заданное максимальное число для вашего приложения. Это число определяется параметром sh--tries, используемом в Artisan-команде shqueue:work. Подробнее о запуске обработчика очереди читайте ниже.

+ 5.3

добавлено в 5.3 ()

Запуск обработчика очереди

Laravel содержит обработчик очереди, который обрабатывает новые задачи при поступлении в очередь. Вы можете запустить его Artisan-командой shqueue:work. Запомните, команда shqueue:work будет выполняться, пока вы не остановите её вручную или закроете свой терминал:

shphp artisan queue:work

Чтобы процесс shqueue:work выполнялся в фоне постоянно, используйте монитор процессов, такой как Supervisor, для проверки, что обработчик очереди не остановился.

Запомните, обработчики очереди — долгоживущие процессы, и они хранят в памяти состояние загруженного приложения. Поэтому они не заметят изменения в вашем коде, сделанные после их запуска. Поэтому в процессе развёртывания не забудьте перезапустить обработчиков очереди.

Указание подключения и очереди

Также вы можете указать, какое подключение должен использовать обработчик очереди. Имя подключения передаётся команде shwork и должно соответствовать одному из подключений, определённых в файле config/queue.php%:

shphp artisan queue:work redis

Можно ещё точнее настроить обработчика очереди, указав для него конкретные очереди в данном подключении. Например, если все ваши email-сообщения обрабатываются в очереди emails в подключении redis, вы можете выполнить такую команду для запуска обработчика только для этой очереди:

shphp artisan queue:work redis --queue=emails

Освобождение ресурсов

Демоны-обработчики очереди не «перезагружают» фреймворк перед обработкой каждой задачи. Поэтому вам надо освобождать все тяжёлые ресурсы после завершения каждой задачи. Например, если вы выполняете обработку изображения с помощью библиотеки GD, то после завершения обработки вам надо освободить память с помощью PHPimagedestroy.

Приоритеты очередей

Иногда бывает необходимо организовать обработку очередей по их приоритетам. Например, в файле config/queue.php вы можете задать очередь low как очередь по умолчанию для подключения redis. При этом при необходимости вы можете помещать задачу в очередь high вот так:

PHP

dispatch((new Job)->onQueue('high'));

Чтобы запустить обработчика, который сначала проверит, что обработаны все задачи из очереди high, и только потом продолжит обрабатывать задачи из очереди low, передайте в команду sh список очередей через запятую:

shphp artisan queue:work --queue=high,low

Обработчики очереди и развёртывание

Поскольку обработчики очереди — долгоживущие процессы, они не подхватят изменения в вашем коде, пока не будут перезапущены. Поэтому простейший способ развернуть приложение с обработчиками очереди — перезапустить обработчиков во время процесса развёртывания. Вы можете мягко перезапустить всех своих обработчиков командой shqueue:restart:

shphp artisan queue:restart

Эта команда заставит обработчиков очереди мягко «умереть» после завершения ими текущей задачи, поэтому ни одна существующая задаче не будет потеряна. Поскольку обработчики очереди умрут при выполнении команды shqueue:restart, вам надо запустить менеджер процессов, такой как Supervisor, для автоматического перезапуска обработчиков очереди.

Окончание срока задачи и таймауты

Окончание срока задачи

В файле config/queue.php для каждого подключения определён параметр retry_after. Этот параметр указывает, сколько секунд подключение должно ждать, прежде чем заново приступить к обрабатываемой задаче. Например, если значение retry_after равно 90, то задача будет возвращена назад в очередь, если пробудет в обработке 90 секунд и не будет удалена. Обычно для retry_after лучше задать значение равное максимальному разумному количеству секунд, которое потребуется задачам для выполнения.

Единственное подключение, у которого нет значения retry_after, — Amazon SQS. SQS пробует выполнить задачу заново исходя из стандартного таймаута видимости, который настраивается в консоли AWS.

Таймауты обработчиков

Artisan-команда shqueue:work имеет параметр sh--timeout. Этот параметр указывает, сколько должен ждать главный процесс обработки очередей Laravel, прежде чем уничтожить дочернего обработчика очереди, выполняющего задачу. Дочерний процесс обработки очереди может «зависнуть» по разным причинам, таким как внешний HTTP-вызов, который не отвечает. Параметр sh--timeout удаляет зависнувшие процессы, превысившие указанный лимит времени:

shphp artisan queue:work --timeout=60

Параметр retry_after в настройках и параметр командной строки sh--timeout разные, но работают совместно для проверки, что задачи не потерялись, и что задачи выполняются успешно только один раз.

Значение sh--timeout должно быть всегда по крайней мере на несколько секунд меньше значения retry_after в настройках. Таким образом обработчик, выполняющий данную задачу, будет всегда убиваться до повторной попытки выполнения задачи. Если ваш параметр sh--timeout имеет значение больше значения retry_after, ваши задачи могут выполниться дважды.

Длительность паузы обработчика

Пока в очереди доступны задачи, обработчик будет выполнять их без задержки между ними. С помощью параметра shsleep можно задать время, на которое будет «засыпать» обработчик, если нет новых задач:

shphp artisan queue:work --sleep=3

+ 5.1 5.0

добавлено в 5.1 () 5.0 ()

Получение задач из запросов

Очень часто переменные HTTP-запросов переносятся в задачи. Поэтому вместо того, чтобы заставлять вас делать это вручную для каждого запроса, Laravel предоставляет несколько вспомогательных методов. Давайте посмотрим на метод PHPdispatchFrom() типажа DispatchesJobs. По умолчанию этот типаж включён в базовый класс контроллера Laravel:

PHP

namespace App\Http\Controllers;

use 

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

class 

CommerceController extends Controller
{
  
/**
   * Обработка данного заказа.
   *
   * @param  Request  $request
   * @param  int  $id
   * @return Response
   */
  
public function processOrder(Request $request$id)
  {
    
// Обработка запроса...$this->dispatchFrom('App\Jobs\ProcessOrder'$request);
  }
}

Этот метод проверит конструктор класса данной задачи и извлечёт переменные из HTTP-запроса (или любого другого объекта ArrayAccess) для заполнения необходимых параметров конструктора задачи. Поэтому, если класс нашей задачи принимает в конструкторе переменную productId, то шина задач попытается получить параметр productId из HTTP-запроса.

Вы также можете передать массив третьим аргументом метода PHPdispatchFrom(). Этот массив будет использован для заполнения тех параметров, которых нет в запросе:

PHP

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

+ 5.0

добавлено в 5.0 ()

Добавление замыканий в очередь

Вы можете помещать в очередь и функции-замыкания. Это очень удобно для простых, быстрых задач, выполняющихся в очереди.

Добавление замыкания в очередь

PHP

Queue::push(function($job) use ($id)
{
  
Account::delete($id);$job->delete();
});

Вместо того, чтобы делать объекты доступными для замыканий в очереди через директиву PHPuse, передавайте первичные ключи и повторно извлекайте связанные модели из вашей задачи в очереди. Это часто позволяет избежать неожиданного поведения при сериализации.

При использовании push-очередей Iron.io будьте особенно внимательны при добавлении замыканий. Конечная точка выполнения, получающая ваше сообщение, должна проверить входящую последовательность-ключ, чтобы удостовериться, что запрос действительно исходит от Iron.io. Например, ваша конечная push-точка может иметь адрес вида https://yourapp.com/queue/receive?token=SecretToken — где значение token можно проверять перед собственно обработкой задачи.

Запуск обработчика очереди

Laravel содержит обработчик очереди, который обрабатывает новые задачи при поступлении в очередь. Вы можете запустить его Artisan-командой shqueue:work. Запомните, команда shqueue:work будет выполняться, пока вы не остановите её вручную или закроете свой терминал:

shphp artisan queue:work

Чтобы процесс shqueue:work выполнялся в фоне постоянно, используйте монитор процессов, такой как Supervisor, для проверки, что обработчик очереди не остановился.

Запомните, обработчики очереди — долгоживущие процессы, и они хранят в памяти состояние загруженного приложения. Поэтому они не заметят изменения в вашем коде, сделанные после их запуска. Поэтому в процессе развёртывания не забудьте перезапустить обработчиков очереди.

Указание подключения и очереди

Также вы можете указать, какое подключение должен использовать обработчик очереди. Имя подключения передаётся команде shwork и должно соответствовать одному из подключений, определённых в файле config/queue.php%:

shphp artisan queue:work redis

Можно ещё точнее настроить обработчика очереди, указав для него конкретные очереди в данном подключении. Например, если все ваши email-сообщения обрабатываются в очереди emails в подключении redis, вы можете выполнить такую команду для запуска обработчика только для этой очереди:

shphp artisan queue:work redis --queue=emails

Освобождение ресурсов

Демоны-обработчики очереди не «перезагружают» фреймворк перед обработкой каждой задачи. Поэтому вам надо освобождать все тяжёлые ресурсы после завершения каждой задачи. Например, если вы выполняете обработку изображения с помощью библиотеки GD, то после завершения обработки вам надо освободить память с помощью PHPimagedestroy.

Приоритеты очередей

Иногда бывает необходимо организовать обработку очередей по их приоритетам. Например, в файле config/queue.php вы можете задать очередь low как очередь по умолчанию для подключения redis. При этом при необходимости вы можете помещать задачу в очередь high вот так:

PHP

dispatch((new Job)->onQueue('high'));

Чтобы запустить обработчика, который сначала проверит, что обработаны все задачи из очереди high, и только потом продолжит обрабатывать задачи из очереди low, передайте в команду sh список очередей через запятую:

shphp artisan queue:work --queue=high,low

Обработчики очереди и развёртывание

Поскольку обработчики очереди — долгоживущие процессы, они не подхватят изменения в вашем коде, пока не будут перезапущены. Поэтому простейший способ развернуть приложение с обработчиками очереди — перезапустить обработчиков во время процесса развёртывания. Вы можете мягко перезапустить всех своих обработчиков командой shqueue:restart:

shphp artisan queue:restart

Эта команда заставит обработчиков очереди мягко «умереть» после завершения ими текущей задачи, поэтому ни одна существующая задаче не будет потеряна. Поскольку обработчики очереди умрут при выполнении команды shqueue:restart, вам надо запустить менеджер процессов, такой как Supervisor, для автоматического перезапуска обработчиков очереди.

Окончание срока задачи и таймауты

Окончание срока задачи

В файле config/queue.php для каждого подключения определён параметр retry_after. Этот параметр указывает, сколько секунд подключение должно ждать, прежде чем заново приступить к обрабатываемой задаче. Например, если значение retry_after равно 90, то задача будет возвращена назад в очередь, если пробудет в обработке 90 секунд и не будет удалена. Обычно для retry_after лучше задать значение равное максимальному разумному количеству секунд, которое потребуется задачам для выполнения.

Единственное подключение, у которого нет значения retry_after, — Amazon SQS. SQS пробует выполнить задачу заново исходя из стандартного таймаута видимости, который настраивается в консоли AWS.

Таймауты обработчиков

Artisan-команда shqueue:work имеет параметр sh--timeout. Этот параметр указывает, сколько должен ждать главный процесс обработки очередей Laravel, прежде чем уничтожить дочернего обработчика очереди, выполняющего задачу. Дочерний процесс обработки очереди может «зависнуть» по разным причинам, таким как внешний HTTP-вызов, который не отвечает. Параметр sh--timeout удаляет зависнувшие процессы, превысившие указанный лимит времени:

shphp artisan queue:work --timeout=60

Параметр retry_after в настройках и параметр командной строки sh--timeout разные, но работают совместно для проверки, что задачи не потерялись, и что задачи выполняются успешно только один раз.

Значение sh--timeout должно быть всегда по крайней мере на несколько секунд меньше значения retry_after в настройках. Таким образом обработчик, выполняющий данную задачу, будет всегда убиваться до повторной попытки выполнения задачи. Если ваш параметр sh--timeout имеет значение больше значения retry_after, ваши задачи могут выполниться дважды.

Длительность паузы обработчика

Пока в очереди доступны задачи, обработчик будет выполнять их без задержки между ними. С помощью параметра shsleep можно задать время, на которое будет «засыпать» обработчик, если нет новых задач:

shphp artisan queue:work --sleep=3

Настройка Supervisor

Установка Supervisor

Supervisor — монитор процессов для ОС Linux, он автоматически перезапустит ваш процесс shqueue:work, если он остановится. Для установки Supervisor в Ubuntu используйте такую команду:

shsudo apt-get install supervisor

Если самостоятельная настройка Supervisor кажется вам слишком сложной, вы можете использовать Laravel Forge, который автоматически установит и настроит Supervisor для ваших проектов Laravel.

Настройка Supervisor

Файлы настроек Supervisor обычно находятся в папке /etc/supervisor/conf.d. Там вы можете создать любое количество файлов с настройками, по которым Supervisor поймёт, как отслеживать ваши процессы. Например, давайте создадим файл laravel-worker.conf, который запускает и наблюдает за процессом queue:work:

conf[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
autostart=true
autorestart=true
user=forge
numprocs=8
redirect_stderr=true
stdout_logfile=/home/forge/app.com/worker.log

В этом примере numprocs указывает, что Supervisor должен запустить 8 процессов queue:work и наблюдать за ними, автоматически перезапуская их при их остановках. Само собой, вам надо изменить часть queue:work sqs директивы command в соответствии с вашим подключением очереди.

Подробнее о Supervisor читайте в его документации.

+ 5.2 5.1 5.0

добавлено в 5.2 () 5.1 () 5.0 ()

Запуск Supervisor

После создания файла настроек вы можете обновить конфигурацию Supervisor и запустить процесс при помощи следующих команд:

shsudo supervisorctl reread

sudo supervisorctl update

sudo supervisorctl start laravel-worker:*