Комментарии 14
Вспомнил Borland Pascal/C++, потоки сообщений Windows3.1 и самописную подсистему реализацию графической среды со своей системой потоков сообщений и «заимствованными» классами объктов из BP.
А не проще было бы просто описать порядок выполнения джобов и не плодить if() {...} ?
Вы про метод next()
? Не думал как тут можно обойтись без условий. Он вызывается из каждого джоба одинаковым способом. Внутри этого метода надо как-то понять, что делать дальше исходя из того, на какой стадии сейчас находимся. От этого и возникают условия. Тут получается этакая фабрика джобов. На мой взгляд наличие условий в этом методе — не страшно. Но если есть идеи как избавиться тут от if-ов, буду рад.
Для начала, какой смысл вызывать один джоб за другим? 1 раз отправили джоб чтобы запрос не висел, а внутри джоба сделать ту же цепочку.
Если все же всё пихать в джобы- вместо if просто сделать карту что за чем идет:
protected static $jobsSequence = [
MyFirstJob::class => MySecondJob::class,
...
]
или сделать просто массив с очередью и использовать поиск по массиву + брать след индекс.
внутри джоба сделать ту же цепочку
Я в статье объяснил, почему это оказалось не удобно.
Насчет карты, можно. Это будет чуть ближе к варианту со штатным withChain()
. Это уже дело техники и предпочтений, и зависит от ситуации. В моем случае, как я упоминал выше, есть элементы бизнес логики, иногда надо создавать 1 джоб, иногда сразу группу джобов, с разными входными данными, тут практичнее использовать условия.
Вот пример. Из внешнего сервиса прилетает новый документ на обработку. Нужно найти в нем ключевые слова, определить их тональность, сделать еще ряд манипуляций. Это делается через очереди. Каждая задача — в своей очереди. И эта цепочка как раз описана в отдельном потомке 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(...))
}
}
}
Управление очередями в Laravel