skeeks / cms-job
Universal background jobs, queue and run history for SkeekS CMS
Package info
Type:yii2-extension
pkg:composer/skeeks/cms-job
Requires
- php: >=8.0
- skeeks/cms: ^6.4.9.25 || dev-master
- yiisoft/yii2-queue: ^2.3.8
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
skeeks/cms-job отвечает за постановку и выполнение фоновых заданий, историю,
прогресс, отмену, повторные попытки и блокировки ресурсов. Расписания принадлежат
skeeks/cms-agent, а запуск и перезапуск служб — хостингу или администратору.
- Канал — именованная очередь, например
catalogилиmaintenance. - Тип задания — зарегистрированная операция с обработчиком, каналом и правилами выполнения.
- Запуск — конкретная операция с payload и записью
CmsJobRunв истории. - Диспетчер — один ожидающий PHP-процесс, запускающий отдельного ребёнка для каждого задания.
Доменному коду не нужно резервировать сообщения, создавать собственный worker
или вызывать классы yii2-queue: используйте Yii::$app->jobs.
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-конфига на лету. После регистрации нового канала или изменения лимитов:
- Убедитесь, что
worker/queues --json=1показывает актуальный канал и его типы. - Корректно остановите диспетчер через SIGTERM и запустите снова. Он прекратит
резервирование и дождётся текущих детей. Для systemd используйте службу,
настроенную с достаточным
TimeoutStopSecиKillMode=mixed. - Если службой управляет
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-процесс себя не перезапускает.
Дальнейшая документация: