Перейти к содержанию

Состав платформы

Фоновые задания

Очередь фоновых заданий, история запусков и прогресс.

Другие решения раздела «Состав платформы»

Установка

Подключите пакет в корне проекта на SkeekS Платформе, затем примените миграции проекта.

composer require skeeks/cms-job

Зависимости

Инструкция из репозитория

Фоновые задания SkeekS CMS

skeeks/cms-job отвечает за постановку и выполнение фоновых заданий, историю, прогресс, отмену, повторные попытки и блокировки ресурсов. Расписания принадлежат skeeks/cms-agent, а запуск и перезапуск служб — хостингу или администратору.

  • Канал — именованная очередь, например catalog или maintenance.
  • Тип задания — зарегистрированная операция с обработчиком, каналом и правилами выполнения.
  • Запуск — конкретная операция с payload и записью CmsJobRun в истории.
  • Диспетчер — один ожидающий PHP-процесс, запускающий отдельного ребёнка для каждого задания.

Доменному коду не нужно резервировать сообщения, создавать собственный worker или вызывать классы yii2-queue: используйте Yii::$app->jobs.

Автоматическое обслуживание

Пакет регистрирует три типа в очереди maintenance: cms-job.cleanup (просроченная история, раз в сутки) и cms-job.cleanup-logs (просроченные приватные логи и CSV отчёты об ошибках, раз в час), cms-job.cleanup-workspaces (временные рабочие папки завершённых заданий, раз в час). При установленном skeeks/cms-agent >= 3.2.5 их расписания входят в общий конфиг пакета. После обновления выполните php yii cmsAgent/init: повторный запуск не создаёт дубли и сохраняет состояние ранее отключённых расписаний. Нужны работающие cmsAgent/execute и потребитель очереди maintenance.

Очистки используют общий ресурсный lock и отдельные ключи дедупликации на всю установку, обрабатывают данные порциями до 500 объектов каждого вида и продолжают тот же запуск, пока просроченные данные не закончатся. Незавершённые задания защищены; действуют существующие сроки хранения. Для новых job со штатными приватными логами дополнительная очистка не нужна. Временные исходники и промежуточные результаты новых job размещайте через $context->getWorkspace()->path('offers.jsonl'). Пакет сохраняет папку между продолжениями, защищает её блокировкой и очищает через 7 суток после успеха или через 14 суток после предупреждений/ошибки/отмены/тайм-аута. API, ограничения, dry-run и переход со старых папок описаны в WORKSPACES.md. Незарегистрированные старые папки, включая supplier-imports, и файлы CMS storage автоматически не удаляются. Подробности внедрения и ручные команды — в DEPLOYMENT.md.

1. Зарегистрировать канал и тип задания

В общем конфиге проекта или пакета-потребителя, который загружают и web, и console, добавьте:

return [
    'components' => [
        'jobQueueFactory' => [
            'queues' => ['examples' => []],
        ],
        'jobRegistry' => [
            'types' => [
                'example.calculate-total' => [
                    'type' => 'example.calculate-total',
                    'title' => 'Подсчёт суммы',
                    'handler' => \app\jobs\CalculateTotalJobHandler::class,
                    'queue' => 'examples',
                    'timeout' => 60,
                    'leaseSeconds' => 30,
                    'idempotent' => true,
                    'maxAttempts' => 3,
                ],
            ],
        ],
    ],
];

Для Composer-пакета подключите этот файл через extra.config-plugin.web и extra.config-plugin.console, как это сделано в composer.json. Не регистрируйте канал только в web-конфиге: консольный диспетчер его не увидит. Ядро объявляет default и maintenance; остальные каналы объявляет потребитель. Регистрация канала сама по себе не запускает процесс и не создаёт подключение БД.

Используйте стабильные имена типов и каналов. Для управления службами хостинга имя канала должно начинаться с буквы/цифры, содержать только буквы, цифры, -, _ и иметь длину до 64 символов. Обработчики должны быть доступны через Composer autoload. Несовместимый payload оформляйте новым типом, например .v2.

2. Написать обработчик

Ниже полностью исполняемый учебный пример без внешних побочных действий:

namespace app\jobs;

use skeeks\cms\job\contracts\JobReporterInterface;
use skeeks\cms\job\handlers\AbstractJobHandler;
use skeeks\cms\job\runtime\JobContext;

final class CalculateTotalJobHandler extends AbstractJobHandler
{
    public function run(JobContext $context, JobReporterInterface $reporter): void
    {
        $values = $context->get('values', []);
        if (!is_array($values)) {
            throw new \InvalidArgumentException('Ожидался список чисел.');
        }
        $reporter->setStage('calculate', 'Подсчёт суммы');
        $reporter->setTotal(count($values));
        $total = 0;
        foreach ($values as $value) {
            $reporter->heartbeat();
            if ($reporter->isCancelled()) { return; }
            if (!is_numeric($value)) {
                throw new \InvalidArgumentException('Список содержит нечисловое значение.');
            }
            $total += $value;
            $reporter->countSuccess();
            $reporter->advance();
        }
        $reporter->setResult(['total' => $total]);
    }
}

В предметном обработчике вызывайте сервис своего пакета. Продлевайте аренду и проверяйте отмену во время долгой работы, а не только перед началом. Перед записью прогресса фиксируйте большие транзакции порциями: heartbeat внутри незавершённой транзакции не виден другим процессам. Не скрывайте ошибки за успешным возвратом из обработчика.

idempotent=true допустим только если повтор после неопределённого результата безопасен. Для необратимых внешних действий оставьте false и одну попытку, пока не реализована надёжная идемпотентность. Лимит на канал не заменяет resourceKey для операций над общими данными и dedupKey/overlapPolicy для повторной постановки. Эти правила задаются в JobTypeDefinition, а не в транспортной очереди.

3. Поставить задание

$run = Yii::$app->jobs->push('example.calculate-total', [
    'values' => [10, 20, 30],
]);
if ($run !== null) {
    $runId = $run->id;
}

В payload передавайте JSON-совместимые значения и идентификаторы, а не модели, соединения или PHP-замыкания. Канал выбирается определением типа. Постановка через штатное общее DB-подключение участвует в транзакции приложения. push() может вернуть null, когда политика пересечений пропустила дубль. Не вставляйте записи прямо в cms_queue или cms_job_run.

Для повторения по расписанию используйте cms-agent с зарегистрированным типом задания; расписание публикует запуск и не выполняет долгий обработчик на web-запросе. Для запуска из UI задайте существующее RBAC-право типа задания и проверьте доступ пользователя; произвольный маршрут не является правом.

4. Запустить диспетчер

Из корня установленного сайта:

php yii cms-job/worker/queues --json=1
php yii cms-job/worker/dispatch

Первая команда показывает итоговую конфигурацию без потребления сообщений. Вторая сама читает jobQueueFactory.queues и опрашивает все каналы, включая пустые. Пустой канал добавляет проверку БД, но не отдельный ожидающий PHP-процесс. В конфигурации по умолчанию php yii cms-job/worker без канала также запускает диспетчер. Привязки к cms-hosting, домену, VPS или конкретному серверу нет. Требуются Linux/PHP с pcntl, транспорт DbQueue и изоляция заданий.

Настройки проекта:

'components' => [
    'jobWorker' => [
        'mode' => 'dispatcher',
        'maxProcesses' => 10,
        'channelConcurrency' => 1,
        'channels' => ['examples' => 2], // необязательное исключение
    ],
],

Разные каналы могут выполняться параллельно. Пределы проверяются до резервирования сообщения. Ребёнок завершается после одного задания; обработка TTR, падения и токенов попыток общая с прежним воркером. В простое адаптер освобождает MySQL-соединение. Локальный lock исключает второй диспетчер этого сайта, но не ограничивает процессы на других серверах.

Когда подключится новый канал

Диспетчер читает конфигурацию при запуске, без перечитывания PHP-конфига на лету. После регистрации нового канала или изменения лимитов:

  1. Убедитесь, что worker/queues --json=1 показывает актуальный канал и его типы.
  2. Корректно остановите диспетчер через SIGTERM и запустите снова. Он прекратит резервирование и дождётся текущих детей. Для systemd используйте службу, настроенную с достаточным TimeoutStopSec и KillMode=mixed.
  3. Если службой управляет cms-hosting с политикой «Все каналы через диспетчер», перезапуск организует автоматическая сверка. Она обнаруживает новые каналы, отключает прежнюю конфигурацию и после завершения заданий включает новую. Штатный интервал сверки — 5 минут; переход может занять несколько сверок и время завершения текущих заданий.

Параметр --queues=examples,maintenance ограничивает диспетчер указанными каналами. Новая очередь вне этого списка не появится после простого рестарта: нужно также обновить список запуска. Хостинг формирует и обновляет его по конфигурации сайта, исключая свой служебный hosting-control.

Не удаляйте канал или тип до обработки/явной отмены старых сообщений и завершения активных запусков. Изменение конфига не переносит накопленные задания.

Совместимость и эксплуатация

Прежние команды сохраняются:

php yii cms-job/worker --queue=maintenance
php yii cms-job/worker/cron --queue=maintenance

Явный --queue всегда запускает отдельный воркер. jobWorker.mode=workers отключает выбор диспетчера для команды без канала. Не запускайте старые воркеры и диспетчер одновременно на одинаковых каналах, если нужны строгие лимиты. Плановый --maxSeconds останавливает приём новых заданий и требует внешнего менеджера процессов для повторного запуска; сам PHP-процесс себя не перезапускает.

Дальнейшая документация:

README на GitHub

Поддержка

Вопросы по установке и работе решения можно задать командой SkeekS или завести обращение в репозитории.

Обновления

v1.3.0

Временные исходники и промежуточные файлы заданий теперь имеют общий жизненный цикл. Обработчик получает путь через JobContext::getWorkspace()->path('offers.jsonl'), а очистку выполняет пакет.

  • Один каталог на запуск; продолжения и автоматические повторы используют прежние файлы.
  • Системная задача cms-job.cleanup-workspaces выполняется ежечасно и допускает ручной запуск. Успешные снимки хранятся 7 суток, остальные завершённые — 14 суток.
  • Активные, ожидающие, удержанные и неоднозначные папки сохраняются. Блокировки защищают от живого старого исполнителя даже после истечения аренды в БД.
  • Карантин на той же файловой системе и журнал позволяют продолжить удаление после сбоя. Есть dry-run и ручное удержание.
  • История сохраняется, пока нужны рабочие ресурсы. Старые проектные папки автоматически не удаляются.
  • Добавлено руководство WORKSPACES.md для разработчиков и эксплуатации.

Проверено: 58 изолированных сценариев рабочих папок, 40 проверок обслуживания и 8 проверок проектного адаптера импортов.

Миграций БД нет. Для системных расписаний нужен cms-agent 3.2.5+. При внедрении необходимо согласованно обновить пакет и обработчики, мягко перезапустить воркеры и применить cmsAgent/init. Поддерживается локальная файловая система с flock и атомарным rename. Старые каталоги требуют отдельной инвентаризации и очистки.

v1.2.0

1.2.0 — Автоматическая очистка истории и логов заданий

В общий конфиг добавлены две системные задачи в очереди maintenance: cms-job.cleanup удаляет просроченную завершённую историю ежедневно, cms-job.cleanup-logs удаляет просроченные приватные логи и CSV-отчёты ежечасно. Обе доступны для ручного запуска в разделе «Расписание».

Для регистрации после обновления выполните php yii cmsAgent/init. Нужны cms-agent >= 3.2.5, действующий вызов cmsAgent/execute и потребитель maintenance. Повторная регистрация не создаёт дубли, сохраняет даты выполнения и состояние отключённых администратором расписаний. После обновления мягко перезапустите постоянные воркеры. Миграций БД нет.

Очистки используют общую блокировку ресурса и отдельные ключи дедупликации на установку. Данные обрабатываются порциями до 500 объектов каждого вида с продолжением того же запуска, проверкой отмены и продлением аренды. Незавершённые задания защищены. Ручные CLI-команды сохранены; их старые расписания следует убрать после перехода, поскольку CLI не берёт блокировку native job.

Произвольные рабочие файлы, включая supplier-imports, и файлы CMS storage в область этих очисток не входят.

Проверки: 33 изолированные проверки на SQLite в памяти без сети, PHP lint, проверка diff. На aney.ru подтверждены системность и активность расписаний, успешные ручные и автоматические запуски обеих задач. Просроченных данных на пилоте не было: фактическое массовое удаление и продолжения проверены на изолированных данных. Полная матрица установки и транспорта в этом релизе повторно не запускалась.

v1.1.0

1.1.0 — Диспетчер очередей сайта

Один ожидающий PHP-процесс обслуживает все каналы сайта и запускает дочерние процессы только для заданий. По умолчанию разрешено до 10 заданий суммарно и одно на канал; лимиты и исключения настраиваются в jobWorker.

Универсальная команда: php yii cms-job/worker/dispatch. Без --queues она читает все каналы из итоговой конфигурации. Новый канал подключается после мягкого перезапуска; конфиг не перечитывается на лету. Хостинг с соответствующей политикой обнаруживает изменение и организует перезапуск автоматически.

Прежние команды с --queue и режим Cron сохранены. Переход требует остановки старых воркеров с завершением текущих заданий. Резервирование остаётся нативным для yii2-queue; общий механизм дочерних процессов сохраняет TTR и обработку падений. Нужны pcntl, изоляция и DbQueue. Миграций нет.

В README добавлена инструкция по регистрации каналов, типов, обработчиков, публикации заданий и подключению новых каналов. JSON discovery сообщает возможность использования диспетчера и его настройки.

Проверено на PHP 8.2 / MariaDB в production-пилоте: 15 транспортных проверок на приватных каналах, 13 проверок DB-соединений, 14 проверок старого воркера/Cron, discovery и PHP-синтаксис. Проверены переходы в обе стороны через хостинг. В замере пилота PSS ожидающих процессов снизился примерно с 82 до 23 МиБ. Дополнительно на одноразовых БД прошли установка и регрессионный набор, обновление старой схемы (83 проверки), защита от конфликта таблиц (80) и установка с префиксом (78). Проверены восстановление после падения, мягкая остановка, отмена, fencing, повторы и мост с расписанием.

v1.0.9

1.0.9

Исправлено удержание подключений MySQL простаивающими воркерами. Раньше каждый канал постоянно занимал DB-сессию, а при изолированном выполнении задания дочерний процесс открывал дополнительное подключение.

Новый транспорт DbQueue закрывает соединение после пустого опроса и освобождает подключение родителя перед запуском дочернего процесса. При следующем запросе Yii открывает соединение заново. Транзакции, mutex резервирования и подтверждение доставки сохранены. Другие драйверы БД используют прежний жизненный цикл.

На пилотном сайте пять каналов сохранили работоспособность, а число подключений в простое снизилось с пяти до нуля в пяти последовательных замерах. Прошли 13 проверок на PHP 8.2 / MariaDB 10.5 и последующие штатные боевые задания. Полный релизный набор на одноразовой БД в этот раз не запускался.

Миграций нет. После обновления перезапустите постоянные воркеры, дождавшись завершения текущих заданий. Явное переопределение класса транспорта в проекте может сохранить старое поведение. Для нестандартных долгоживущих блокировок или состояния DB-сессии предусмотрен releaseIdleConnection=false. Требуются непостоянные PDO-подключения. Число PHP-процессов не уменьшается.

v1.0.8

Воркеры автоматически проверяют просроченные задания при старте и между обработками, не чаще раза в минуту. Безопасные повторные попытки возвращаются в очередь; отменённые и исчерпавшие попытки задания завершаются и перестают блокировать расписание. Действующие аренды не затрагиваются.

Проверено: 78 изолированных проверок установки, регрессионный набор очередей и восстановления, синтаксис PHP.

После обновления пакета штатно перезапустите воркеры. Если все воркеры заняты или остановлены, для независимого восстановления можно оставить периодический cms-job/worker/reap. Новых миграций нет.

v1.0.7

В списке фоновых заданий теперь отображается понятное сообщение прогресса: сколько обработано, что выполняется или какой результат получен.

Если сообщение отсутствует, сохраняется прежний вывод этапа. Проценты и счётчики остаются на месте; текст безопасно экранируется.

Проверено: синтаксис PHP и 23 проверки отображения статусов.

v1.0.6

Локальные задания, выполняемые порциями, теперь могут отображать «Ожидает продолжения» между доставками вместо обычного «В очереди».

Обработчик перед повторной постановкой сохраняет в результате _job_execution.state = awaiting_continuation. Во время выполнения отображается «Выполняется», после завершения — итоговый статус. Первичная постановка без маркера сохраняет прежнее поведение.

Технические статусы, блокировки, повторы и отмена не изменены. Миграции не требуются. Проверены 23 сценария отображения статусов и 67 проверок интеграции с XML-импортом.

v1.0.5

Дочерний PHP-процесс теперь наследует действующий memory_limit родительского воркера. После подтверждённого аварийного завершения дочернего процесса родитель сразу фиксирует результат вместо ожидания уборки истёкшей аренды. Запрошенная отмена завершает задание без повторного запуска. Сохранены проверка токена попытки и ограничения повторов для идемпотентных заданий. Добавлены регрессионные проверки памяти, отмены, аварий и защиты от изменения чужой попытки. После установки необходимо корректно перезапустить воркеры. Уже зависшие задания требуют отдельной проверки; релиз автоматически их не исправляет.

v1.0.4

Исправлено отображение удалённых операций: выполняющееся задание больше не показывается ожидающим в очереди между проверками. При устаревших данных выводится «Статус уточняется». Обновляются статус в карточке и индикатор кнопки. Механика очереди и повторных попыток не изменена. Добавлены проверки состояний и интерфейса.

v1.0.3

JobButton supports primary actions and persistent progress for remote operations between queue polls. Existing consumers retain secondary styling. Verified with the job button UI regression suite.

Показать ещё 3 обновления