Ещё один cron — самый дешёвый способ сказать «пусть это будет не в веб-запросе». Часто граница выбрана неверно. Команда в планировщике Laravel может слать почту, пересобирать отчёт или опрашивать чужой API, пока два запуска не наложатся и повтор не сделает побочный эффект второй раз. Процесс на Node рядом с этим приложением уместен, когда код уже живёт на Node или когда повторы и падения задачи не должны делить процесс с сайтом. Это не лечение медленного запроса.
Решение идёт в таком порядке. Что нельзя оставлять в веб-запросе. Что должно получиться один раз, даже если задачу запустят снова. И только потом — на чём это выполнять. Ситуации ниже узнаваемы. Это не сравнение скорости Node и PHP и не повод резать маленькое приложение надвое, потому что на схеме два квадрата.
1. Что cron на самом деле скрывает
В запланированную команду врастает целая задача. Расписание срабатывает каждую минуту. Команда выбирает строки и делает работу прямо внутри себя. Пока она ещё идёт, следующая минута срабатывает снова. Получаются два воркера, которые друг о друге не знают. База общая, и внешний API тоже тот, который команда вызывает.
Cron уже есть. Планировщик Laravel — привычное место для «сделай это позже». «Позже» превращается в «пройди всех клиентов в системе, потом позови третью сторону, потом запиши отчёт». Наложение незаметно, пока клиент не получит два письма или оценку не поставят дважды. Блокировка вокруг команды останавливает наложение. Повтора с правилами, отдельной очереди для сломанных задач и места для запуска, который длиннее, чем блокировка готова ждать, она не даёт.
Посмотрите на команду, которая уже есть:
Если честный ответ — «одна единица работы, её можно запустить снова, она длиннее запроса», вам нужна очередь. Очередь может остаться ларавелевской, на Redis или на базе, с воркером, который вы и так умеете поднимать. Уходить с PHP — следующее решение, не это.
2. Когда воркером стоит сделать Node
Разрез «потому что так красивее» дорого обходится. «Node лучше умеет асинхронность» — не архитектура. Node уместен, когда нужный код уже на Node или когда падение этой задачи должно быть отдельно от выката Laravel: долгий обработчик, библиотека, которая есть только в этой экосистеме, процесс, который можно перезапустить, не трогая процессы сайта.
Смешанные стеки появляются, потому что сайт и побочная работа не начинались одной кодовой базой. На одной CRM, где несколько клиентов живут в одном приложении, продукт остался на PHP, а отдельный процесс вёл обработку звонка: сборку промпта, структурированный ответ модели, отчёт, плюс телефонию, сообщения и учёт. Задачи шли через очередь. Повтор находил работу уже записанной и останавливался. Второго результата не появлялось. Границей была обработка звонка. Это не заявление, что Node быстрее команды Laravel. Цифру «очередь против cron» по той системе я бы не публиковал: такого замера работа не дала.
На унаследованных боевых системах осторожность с другой стороны. Обе среды выполнения уже могут лежать в репозитории. Решают, что привести в порядок. Второй процесс не заводят только потому, что Node оказался установлен.
До того как добавлять процесс, зафиксируйте:
3. Повтор решает больше, чем язык
Обработчик на любом языке, который принимает «хотя бы один раз» за «ровно один раз», удвоит побочный эффект. Брокер доставляет снова. Процесс падает после побочного эффекта и до подтверждения. Вторая доставка снова списывает, шлёт письмо или вставляет строку.
Сначала пишут основной путь. Обработчик зовёт провайдера, потом пишет строку. Падение между этими двумя шагами — дубль. Безопасный повтор значит: вторая доставка находит работу уже записанной и останавливается. На обработке звонка выше это было обязательным. Оно так же обязательно, если воркер — очередь Laravel на Redis. Язык это за вас не сделает. Проверку пишете вы: ключ этого звонка, этого события, этого списания и результат, который можно прочитать обратно.
Прежде чем доверять повтору:
Филиал или клиент в общей системе принадлежат тому же решению. На той CRM у каждого филиала была своя база, миграции и индексы строили под разделение. Воркер, который открывает «ту самую» базу и верит сообщению, легко запишет строки одного клиента задачей другого. Очередь этого не чинит. В сообщении должен быть клиент, а воркер должен отказаться от соединения, которое с ним не совпадает.
4. Очередь не вместо чтения запроса
Медленный запрос, который «чинят» переносом на Node, был медленным из-за того, как читают данные. Воркер выполняет те же запросы и отдаёт тот же объём. Человек теперь смотрит на загрузку с подписью «обрабатывается», а счёт за CPU переезжает в другой сервис.
Очередь заметна в архитектуре. Индекс — нет. На одной учебной платформе в стеке уже были и Laravel, и Node. Замеренные изменения были не про нового воркера. Цикл запросов к базе стал одним агрегирующим запросом: операция сократилась с 12,41 до 2,451 секунды, память упала на 1,5 ГБ. Средняя нагрузка CPU за неделю снизилась с 82,2% до 2,75%, когда разобрали узкие места, включая поиск без индексов. Критическая операция — с 23 937 мс до 48 мс. Один ответ API — с 912 КБ до 2,1 КБ. Каждая цифра про свою операцию. На всю платформу их не переносят. Очередь на Node рядом с Laravel не была бы первой правкой для цикла на двенадцать секунд. Цикл переехал бы, всё ещё примерно на двенадцать секунд, в процесс, который труднее открыть глазами.
В другом месте убрали зависший интерфейс, и критический процесс сократился примерно с часа до шести минут. Это результат того процесса. Доводом в пользу второй среды выполнения он не служит.
Сначала прочитайте медленную операцию. Для человека, который план запроса не откроет, та же привычка изложена в материале что замерить до редизайна. Если тормозит отчёт, а не страница, версия для владельца продукта — очереди и индексы, когда отчёт в CRM долго открывается.
5. Три разные ситуации
Ситуацию не смешивайте с соседней. История одного продукта легко становится общим шаблоном. Не переносите её, если у вас другая.
Первая. Очередь рядом с PHP-приложением для работы, которую надо повторять без дублей. Сайт остаётся на месте. Обработчик ведёт длинную цепочку: оценка звонка, разговор с телефонией, запись отчёта. Граница — сообщение. Копировать стоит границу и правило повтора, а не обязательного брокера.
Вторая. Node — само приложение, не процесс сбоку. Продукт уже сервис на Node, со своим интерфейсом. Работа по скорости — утечки памяти и медленные запросы на этом процессе. Выкат — сборка, которая доезжает туда, где продукт реально работает. Laravel-приложения, которое ждёт воркера, нет. Диагностику оставляют на процессе, который обслуживает продукт. Вторую среду не заводят, чтобы на схеме было два квадрата.
Третья. Фоновая работа уже стоит рядом с запросом, иногда на том же языке. У API может быть свой запуск задач без второй экосистемы. Поисковый индекс, который сжимается с гигабайтов до нескольких мегабайтов, — это изменение индекса. Воркером оно бы не стало. На маленькой системе вопрос тот же: что именно медленно и нужен ли ещё один процесс вообще.
Когда просить разобрать границу вместе
Если вы можете назвать задачу, правило повтора и причину, почему воркер на Node или не на Node, второе мнение не нужно, чтобы поставить блокировку на cron. Зовите, когда повтор может списать деньги или уведомить дважды, а обработчик это не предотвращает, когда Laravel-воркер и обработчик на Node оба поедут в выкат и за сообщение никто не отвечает, или когда медленный запрос собираются перенести, а не прочитать.
Такую границу я разбираю внутри уже существующих систем на Laravel и Node, в том числе когда обе уже на бою. Услуги — скорость, SaaS-бэкенды и интеграции, которые обычно эти задачи и порождают. Проекты держат каждую из этих ситуаций в своём объёме и не превращают ни одну в общее «ускорили». Если нужен разбор одной команды, которая переросла cron, напишите мне в LinkedIn. Пришлите имя задачи, чего повтор не должен сделать второй раз, и на чём код уже написан.