В процессе обработки заданий могут возникать исключительные ситуации. Это могут быть как внутренние ошибки — результат криво написанного кода, так и внешние, когда недоступны запрашиваетмые сервисы и внешние ресурсы. Во втором случае неплохо иметь возможность повторить попытку выполнить задание через некоторое время.
Для того чтобы это сделать существует несколько способов.
Первый способ реализован глобальными настройками компонента:
'components' => [
'queue' => [
'class' => \zhuravljov\yii\queue\<driver>\Queue::class,
'ttr' => 5 * 60, // Максимальное время выполнения задания
'attempts' => 3, // Максимальное кол-во попыток
],
],
Опция ttr
устанавливает резервное время для выполнения заданий в очереди. Если в течении этого
времени задание не выполнилось, оно вернется в очередь на повторную попытку. Опция attempts
устанавливает максимальное кол-во попыток. Если попытки закончились, а задание не выполнено, оно
будет удалено из очереди как выполненное.
Описанные опции действуют глобально на все задания в очереди, и чтобы для отдельных заданий это поведение переопределить существует второй способ.
Индивидуальный контроль повторного выполнения реализован интерфейсом RetryableJob
, код такого
job-объекта может выглядеть так:
class SomeJob extends Object implements RetryableJob
{
public function execute($queue)
{
//...
}
public function getTtr()
{
return 15 * 60;
}
public function canRetry($attempt, $error)
{
return ($attempt < 5) && ($error instanceof TemporaryException);
}
}
Методы getTtr()
и canRetry()
имеют более высокий приоритет чем общие настройки очереди, и дают
возможность реализовать индивидуальный алгоритм повторного выполнения задачи если предыдущая попытка
завершилась неудачей.
Третий способ задать резервное время и необходимость повторного запуска невыполненной задачи
предполагает использовать события Queue::EVENT_BEFORE_PUSH
и Queue::EVENT_AFTER_ERROR
.
Событие Queue::EVENT_BEFORE_PUSH
можно использовать, чтобы задать резервное время:
Yii::$app->queue->on(Queue::EVENT_BEFORE_PUSH, function (PushEvent $event) {
if ($event->job instanceof SomeJob) {
$event->ttr = 300;
}
});
А событие Queue::EVENT_AFTER_ERROR
— чтобы определить задание на повторную попытку:
Yii::$app->queue->on(Queue::EVENT_AFTER_ERROR, function (ErrorEvent $event) {
if ($event->job instanceof SomeJob) {
$event->retry = ($event->attempt < 5) && ($event->error instanceof TemporaryException);
}
});
Обработчики событий выполняются после методов RetryableJob
, и, следовательно, имеют наивысший
приоритет.
Не все драйверы поддерживают повторное выполнение одинаково хорошо. Полнеценную поддержку обеспечивают драйвера: Beanstalk, DB, File и Redis. Синхронный драйвер, как отладочный, не будет повторять невыполненные задания. Gearman не поддерживает повторное выполнение вообще. А RabbitMQ имеет только свою базовую поддержку повторов, при которой номер попытки узнать не получится.