Чтобы использовать расширение в своем проекте, необходимо дополнить конфигурацию следующим образом:
return [
'bootstrap' => [
'queue', // Компонент регистрирует свои консольные команды
],
'components' => [
'queue' => [
'class' => \zhuravljov\yii\queue\<driver>\Queue::class,
'as log' => \zhuravljov\yii\queue\LogBehavior::class,
// Индивидуальные настройки драйвера
],
],
];
Список доступных драйверов и инструкции по их настройке можно посмотреть в содержании.
Каждое задание, которое Вы планируете отправлять в очередь, оформляется в виде отдельного класса. Например, если нужно скачать и сохранить файл, класс может выглядеть так:
class DownloadJob extends Object implements \zhuravljov\yii\queue\Job
{
public $url;
public $file;
public function execute($queue)
{
file_put_contents($this->file, file_get_contents($this->url));
}
}
Отправить задание в очередь можно с помощью кода:
Yii::$app->queue->push(new DownloadJob([
'url' => 'http://example.com/image.jpg',
'file' => '/tmp/image.jpg',
]));
Отправить задание для выполнения с задержкой в 5 минут:
Yii::$app->queue->delay(5 * 60)->push(new DownloadJob([
'url' => 'http://example.com/image.jpg',
'file' => '/tmp/image.jpg',
]));
Внимание: Драйвера RabbitMQ и Gearman не поддреживают отложенные задания.
Способы обработки очереди задач могут различаться, и зависят от используемого драйвера. В большей части драйверов воркеры запускаются с помощью консольных команд, которые компонент сам регистрирует в приложении. Детальное описание смотрите в документации конкретного драйвера.
Компонент дает возможность отслеживать состояние поставленных в очередь заданий.
// Отправляем занание в очередь, и получаем его ID.
$id = Yii::$app->queue->push(new SomeJob());
// Задание еще находится в очереди.
Yii::$app->queue->isWaiting($id);
// Воркер взял задание из очереди, и выполняет его.
Yii::$app->queue->isReserved($id);
// Воркер уже выполнил задание.
Yii::$app->queue->isDone($id);
Внимание: Драйвер RabbitMQ не поддерживает статусы.
В очередь можно передавать гибридные данные, например:
Yii::$app->queue->push([
'function' => 'download',
'url' => 'http://example.com/image.jpg',
'file' => '/tmp/image.jpg',
]);
Это допустимо, если очередь обрабатывается сторонним воркером, разработанным индивидуально.
Если воркер разрабатыватеся не на PHP, то, также, необходимо изменить способ сериализации данных, чтобы сообщения кодировались доступным воркеру форматом. Например, настроить сериализацию в json, самым простым способом, можно так:
return [
'components' => [
'queue' => [
'class' => \zhuravljov\yii\queue\<driver>\Queue::class,
'serializer' => \zhuravljov\yii\queue\serializers\JsonSerializer::class,
],
],
];
Очередь триггерит следующие события:
Событие | Класс события | Когда триггерится |
---|---|---|
Queue::EVENT_BEFORE_PUSH | PushEvent | Добавление задания в очередь используя метод Queue::push() |
Queue::EVENT_AFTER_PUSH | PushEvent | Добавление задания в очередь используя метод Queue::push() |
Queue::EVENT_BEFORE_EXEC | JobEvent | Перед каждым выполнением задания |
Queue::EVENT_AFTER_EXEC | JobEvent | После каждого успешного выполнения задания |
Queue::EVENT_AFTER_EXEC_ERROR | ErrorEvent | Если при выполнение задания случилось непойманное исключение |
Вы с лёгкостью можете подключить свой собственный слушатель на любое из этих событий. Например, давайте отложим выполнение задания, которое выбросило специальное исключение:
Yii::$app->queue->on(Queue::EVENT_AFTER_EXEC_ERROR, function ($event) {
if ($event->error instanceof TemporaryUnprocessableJobException) {
$queue = $event->sender;
$queue->delay(7200)->push($event->job);
}
});
Этот компонент предоставляет LogBehavior для логирования событий, используя встроенный в Yii логгер.
Чтобы использовать его, просто подключите это поведение в конфигурации компонента, как показано в примере:
return [
'components' => [
'queue' => [
'class' => \zhuravljov\yii\queue\redis\Queue::class,
'as log' => \zhuravljov\yii\queue\LogBehavior::class
],
],
];
Пример настройки:
return [
'bootstrap' => [
'queue1', // Первый компонент регистрирует свои консольные команды
'queue2', // Второй - свои
],
'components' => [
'queue1' => [
'class' => \zhuravljov\yii\queue\redis\Queue::class,
],
'queue2' => [
'class' => \zhuravljov\yii\queue\db\Queue::class,
'serializer' => \zhuravljov\yii\queue\serializers\JsonSerializer::class,
],
],
];
Пример использования:
// Отправка задания в очередь для обработки встроенным воркером
Yii::$app->queue1->push(new DownloadJob([
'url' => 'http://example.com/image.jpg',
'file' => '/tmp/image.jpg',
]));
// Отправка сообщения в другую очередь для обработки сторонним воркером
Yii::$app->queue2->push([
'function' => 'download',
'url' => 'http://example.com/image.jpg',
'file' => '/tmp/image.jpg',
]);
Используя очереди важно помнить, что задачи ставятся и извлекаются из очереди в разных процессах. Поэтому, при обработке задания, избегайте использования внешних зависимостей, когда не уверены в том, что они будут доступны в окружении, где работает воркер.
Все данные, необходимые для выполнения задания, нужно оформлять в виде свойств Вашего job-объекта, и отправлять в очередь вместе с ним.
Если в задании нужно работать с моделью ActiveRecord
, вместо самой модели передавайте ее ID. А в
момент выполнения извлекайте ее из базы данных.
Например:
Yii::$app->queue->push(new SomeJob([
'userId' => Yii::$app->user->id,
'bookId' => $book->id,
'someUrl' => Url::to(['controller/action']),
]));
Класс задания:
class SomeJob extends Object implements \zhuravljov\yii\queue\Job
{
public $userId;
public $bookId;
public $someUrl;
public function execute($queue)
{
$user = User::findOne($this->userId);
$book = Book::findOne($this->bookId);
//...
}
}