Как стать автором
Обновить

Комментарии 14

Идут годы, а ничего не меняется.
Вспомнил Borland Pascal/C++, потоки сообщений Windows3.1 и самописную подсистему реализацию графической среды со своей системой потоков сообщений и «заимствованными» классами объктов из BP.

А не проще было бы просто описать порядок выполнения джобов и не плодить if() {...} ?

Вы про метод next()? Не думал как тут можно обойтись без условий. Он вызывается из каждого джоба одинаковым способом. Внутри этого метода надо как-то понять, что делать дальше исходя из того, на какой стадии сейчас находимся. От этого и возникают условия. Тут получается этакая фабрика джобов. На мой взгляд наличие условий в этом методе — не страшно. Но если есть идеи как избавиться тут от if-ов, буду рад.

Для начала, какой смысл вызывать один джоб за другим? 1 раз отправили джоб чтобы запрос не висел, а внутри джоба сделать ту же цепочку.
Если все же всё пихать в джобы- вместо if просто сделать карту что за чем идет:


protected static $jobsSequence = [
   MyFirstJob::class => MySecondJob::class,
   ...
]

или сделать просто массив с очередью и использовать поиск по массиву + брать след индекс.

внутри джоба сделать ту же цепочку

Я в статье объяснил, почему это оказалось не удобно.
Насчет карты, можно. Это будет чуть ближе к варианту со штатным withChain(). Это уже дело техники и предпочтений, и зависит от ситуации. В моем случае, как я упоминал выше, есть элементы бизнес логики, иногда надо создавать 1 джоб, иногда сразу группу джобов, с разными входными данными, тут практичнее использовать условия.

Можете привести конкретный пример из жизни где вы используете чаининг джобов?

Вот пример. Из внешнего сервиса прилетает новый документ на обработку. Нужно найти в нем ключевые слова, определить их тональность, сделать еще ряд манипуляций. Это делается через очереди. Каждая задача — в своей очереди. И эта цепочка как раз описана в отдельном потомке PipelineAbstract.


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

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

Да, можно и так. Но у меня жесткого связывания нет, логику управления задачами я как раз вынес за пределы задач. С листенерами получится примерно так же — надо передавать данные и информацию о том, в рамках какого процесса сейчас идет обработка данных, если нужно, чтобы одну и ту же задачу можно было использовать в разных процессах.

Ваши задачи, кроме основного функционала, зашита передача управления следующей задаче. Это лишняя ответственность. И еще зависимость от PipelineAbstract. Задача должна инициировать исключение если что-то пошло не так, и, если надо, инициировать соответствующее событие, возможно, несколько. А соотетствующие листенеры будут запускать следующую задачу.
Тогда задачи и листенеры будут чистыми, атомарными, легко тестируемыми и т.п. Вам не нужно будет что-то в них переписывать при изменении логики обработки последовательности. Нужно лишь будет изменить маппинг в Эвент провайдере.

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


Например, взвесив за и против, для нынешнего проекта я предпочту вариант с pipeline. Для другого проекта с еще более витиеватой логикой возможно лучше подойдет вариант с листенерами.

Ответ в последнем абзаце.

Был похожий кейс, чейны не подходили потому что в случае фейла на одном из шагов нужна была возможность перезапустить задачу с конкретного шага
Поэтому в Job передавался параметр названия шага, и перезапускал сам себя со следующим шагом.


Пример
class TaskPayload
{
     public $step;
     public $data;
     public function __construct($data, $step = 'default'){  ... }
}

class WholeTaskJob implements ShouldQueue
{
    use Dispatchable;

    protected $payload;

    public function __construct(TaskPayload $payload)
    {
        $this->payload = $payload;
    }

    public function handle(MyTaskService $service, MyTaskStorage $storage)
    {
        $storage->updateStep($this->payload)
        $nextPayload = $service->run($this->payload);
        if($nextPayload){
             static::dispatch($nextPayload);
        }else{
             event(new JobFinished(...))
        }
    }
}
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории