Почти на любом интернет-магазине есть работа, которую нельзя привязать к нажатию кнопки посетителем: разобрать очередь писем, забрать заказы и остатки из 1С, пересчитать бонусы, обновить курсы валют, почистить временные файлы, сгенерировать выгрузку для маркетплейса. Всё это — задачи по расписанию. В 1С-Битрикс за них отвечают агенты — встроенный в ядро механизм периодических задач. Но у агентов есть коварная особенность: по умолчанию они часто запускаются «на хитах», то есть на запросах реальных посетителей, — и тогда ночью на малопосещаемом сайте ваш обмен с 1С может просто не сработать, а тяжёлый агент замедлит страницу живому человеку.
Эта статья — практическая карта регламентных задач в Битриксе. Разберём, что такое агенты и как устроен их жизненный цикл (интервал, next_exec, признак периодичности), чем режим «агенты на хитах» отличается от «агентов на cron» и почему для продакшена почти всегда выбирают cron. Покажем настройку cron через bitrix/php_interface и cron_events.php, пошаговый перевод агентов на cron, создание агента через CAgent::AddAgent, идемпотентность и блокировки от повторного запуска, очередь писем и почтовые события по расписанию, обмен с 1С, вынос тяжёлых фоновых задач в отдельный процесс и очередь, пул задач и параллельность, а также отладку «агент не запускается» и мониторинг. Материал — выжимка из нашей практики поддержки проектов на 1С-Битрикс, где стабильность фоновых задач напрямую влияет на то, вовремя ли уходят письма и обновляются ли остатки.
Коротко
- Агенты — периодические задачи в ядре Битрикса: функция + модуль + интервал + время следующего запуска (
next_exec). Через них идут рассылки, обмен, пересчёты и чистки. - Есть два режима запуска: на хитах (зависит от трафика, тормозит посетителя) и на cron (по расписанию ОС, независимо от посетителей). Для продакшена — cron.
- Перевод на cron: отключить агенты на хитах в
bitrix/php_interfaceи добавить в crontab регулярный вызовcron_events.php. - Тяжёлые фоновые задачи не гоняют в агенте на хите — их порционируют или выносят в отдельный процесс/очередь; агент лишь ставит задание.
- Надёжность — это идемпотентность (повторный запуск безопасен), блокировки (нет двойного выполнения) и мониторинг «когда агент отработал в последний раз».
Зачем нужны задачи по расписанию
Веб по своей природе синхронный: пришёл запрос — сервер отдал ответ. Но у магазина полно работы, которая не привязана ни к какому конкретному запросу и должна выполняться сама по себе, регулярно. Отправить накопившиеся письма. Забрать из 1С новые цены. Пересчитать рейтинги товаров. Удалить корзины брошенных сессий. Сформировать ночную выгрузку. Всё это — регламентные задачи, и их нельзя «повесить» на действие пользователя, потому что пользователя в нужный момент может просто не быть.
Есть два принципиально разных способа выполнять такую работу:
- Планировщик операционной системы (cron) — системная служба Unix, которая по расписанию запускает указанные команды: «каждую минуту», «каждый час», «в 3:00 по будням». Это надёжный, независимый от веб-трафика механизм.
- Внутренний механизм приложения — в Битриксе это агенты: платформа сама ведёт список задач и время их запуска, а «толчок» к выполнению получает либо от посетительских хитов, либо от того же cron.
Ключевая мысль, которую стоит усвоить сразу: агент — это задача, а не процесс. Агент не «крутится в фоне» сам по себе. Он лежит записью в таблице и ждёт, пока кто-то (хит или cron) не проверит: «не пора ли?». Понимание этого снимает половину вопросов о том, почему агенты иногда не срабатывают. Разберёмся детально.
Что такое агенты Битрикса
Агент в 1С-Битрикс — это запись о периодической задаче в системной таблице агентов (b_agent). Каждая запись описывает, что вызывать и когда. Основные атрибуты агента:
| Поле | Что означает |
|---|---|
NAME | Строка вызова — имя функции или метода, который выполняется (например \Vendor\Shop\Cron::syncStocks();) |
MODULE_ID | Модуль, к которому привязан агент (агенты удаляются вместе с модулем) |
ACTIVE | Активность (Y/N) — неактивный агент не запускается |
AGENT_INTERVAL | Интервал повторения в секундах (3600 — раз в час, 86400 — раз в сутки) |
NEXT_EXEC | Дата и время следующего запланированного запуска |
LAST_EXEC | Дата и время последнего фактического запуска |
IS_PERIOD | Признак строгой периодичности (держать запуски по сетке кратно интервалу) |
SORT | Порядок выполнения относительно других агентов |
Функция агента — это обычная PHP-функция или статический метод. Она делает свою работу (шлёт письма, синхронизирует данные) и возвращает строку со своим же вызовом — чтобы ядро знало, как перепланировать её на будущее. Управляют агентами через класс CAgent (AddAgent, RemoveAgent, Update) или через административный раздел «Настройки → Инструменты → Агенты», где виден список всех регламентных задач с их интервалами и временем следующего запуска.
Жизненный цикл агента: next_exec и интервал
Чтобы уверенно работать с агентами, нужно понимать их жизненный цикл. Он простой, но в деталях кроется большинство сюрпризов «почему запустилось не тогда».
Когда наступает момент проверки (на хите или по cron), ядро выбирает из таблицы агенты, у которых ACTIVE='Y' и NEXT_EXEC <= текущего времени, и выполняет их по порядку. После выполнения агента ядро вычисляет новое NEXT_EXEC. Вот здесь и работает признак периодичности:
- Непериодический агент (
IS_PERIOD='N'): следующий запуск = время фактического выполнения + интервал. Если запуск задержался (например, не было хитов), расписание «сдвигается» — интервал отсчитывается от реального момента, а не от плана. - Периодический агент (
IS_PERIOD='Y'): ядро старается держать запуски на сетке, кратной интервалу от точки отсчёта. Пропущенные интервалы не «догоняются» пачкой — планируется ближайший узел сетки в будущем.
Практический вывод: агент не гарантирует запуск «точно в 3:00». Он гарантирует «не раньше NEXT_EXEC и при первой возможности после». Если вам нужна привязка к конкретному времени суток, надёжнее не агент с суточным интервалом, а отдельный cron-скрипт, который системный планировщик вызовет ровно в назначенный час. Агенты хороши для «примерно каждые N минут/часов», а не для «ровно в момент X».
Два режима: агенты на хитах и на cron
Один и тот же агент может запускаться двумя способами — и это, пожалуй, главное, что нужно понять про регламентные задачи Битрикса.
Режим «на хитах» (по умолчанию). Битрикс проверяет и выполняет агенты в рамках обычных посетительских запросов. Пришёл человек на сайт — ядро в этом же хите между делом смотрит: не пора ли выполнить какой-нибудь агент? Если пора — выполняет прямо здесь, «на теле» чужого запроса. Настройка «Автоматически проверять активность агентов и рассылку почты на хитах» в модуле main как раз про этот режим.
Режим «на cron». Агенты запускает не посетитель, а системный планировщик операционной системы. В crontab добавлена команда, которая раз в минуту вызывает специальный PHP-скрипт Битрикса; он-то и проверяет/выполняет накопившиеся агенты и разбирает почтовую очередь. Посетительские хиты при этом от агентов освобождаются.
Различие фундаментальное. В первом случае выполнение задач привязано к трафику и выполняется в контексте посетителя. Во втором — идёт по независимому расписанию в отдельном процессе. Отсюда и вытекает вся разница в надёжности, которую разберём дальше.
Почему cron надёжнее хитов
Перевод агентов на cron — стандартная рекомендация для любого боевого проекта на Битриксе. Причин несколько, и все они практические.
- Независимость от трафика. На хитах агент физически не запустится, если на сайт никто не заходит. Ночью, в выходные, на новом или узкоспециальном B2B-магазине трафик может падать до нуля — и обмен с 1С, и рассылка «зависают» до утра. Cron дёргает систему по часам, а не по посетителям.
- Не страдает посетитель. Тяжёлый агент, выполняясь на хите, добавляет своё время выполнения к загрузке страницы конкретного человека. Он ждёт, пока отработает чужая фоновая задача. На cron агенты выполняются в отдельном процессе и на скорость страниц не влияют.
- Предсказуемое расписание. Cron запускается строго по времени, поэтому интервалы между выполнениями ровные. На хитах «раз в час» превращается в «когда в очередной час зайдёт первый посетитель после наступления next_exec».
- Изоляция ошибок и ресурсов. Процессу cron можно задать свои лимиты (память, время выполнения), отделив тяжёлые фоновые задачи от веб-настроек. Падение фоновой задачи не отражается на отдаче страниц.
- Централизованный запуск почты. Разбор очереди почтовых событий тоже висит на агентском механизме. На cron письма уходят стабильными порциями, а не «когда придёт трафик».
Обратная сторона у cron одна: его нужно настроить и следить, что он работает. Если crontab не настроен или сломался, а агенты на хитах вы уже отключили, то регламентные задачи не выполнятся вообще — и это тихая, незаметная авария. Поэтому cron всегда идёт в комплекте с мониторингом «когда в последний раз отработал», о котором поговорим в конце.
Таблица: агент на хите против агента на cron
Компактное сравнение двух режимов, чтобы держать выбор под рукой:
| Критерий | Агенты на хитах | Агенты на cron |
|---|---|---|
| Кто даёт «толчок» | Запрос посетителя | Планировщик ОС (cron) |
| Зависимость от трафика | Полная: нет хитов — нет запуска | Нет: работает и без посетителей |
| Влияние на скорость страниц | Тормозит хит, в котором выполняется | Не влияет — отдельный процесс |
| Равномерность интервалов | Плавает, зависит от посещений | Стабильная, по расписанию |
| Ночные и редкие задачи | Ненадёжно | Надёжно |
| Лимиты выполнения | Веб-настройки PHP | Свои лимиты CLI-процесса |
| Настройка | Из коробки, ничего не нужно | Нужен crontab + отключение хитов |
| Риск «тихой» аварии | Задачи всё же идут при трафике | Сломался cron — не идёт ничего |
| Рекомендация | Только dev / низкие требования | Продакшен по умолчанию |
Вывод из таблицы прямой: для реального магазина cron — стандарт, хиты — временное решение или режим для локальной разработки. Единственная плата за cron — обязательный мониторинг того, что планировщик жив.
Настройка cron: php_interface и cron_events.php
Технически перевод на cron состоит из двух частей: сказать Битриксу «не запускай агенты на хитах, я это делаю через cron» и добавить в системный планировщик регулярный вызов Битрикса. Первую часть настраивают в служебных файлах каталога bitrix/php_interface.
В файле bitrix/php_interface/dbconn.php определяют константу, которая переводит платформу в «режим cron» для агентов и почты. Классический вариант:
<?php
// bitrix/php_interface/dbconn.php
// Переводим агентную подсистему в режим управления по cron:
// агенты и рассылка почты больше не запускаются на хитах посетителей.
define("BX_CRONTAB_SUPPORT", true);
// Флаг того, что текущий запуск — именно cron-контекст,
// проставляется скриптом cron_events.php (см. ниже).
if (!defined("BX_CRONTAB")) {
define("BX_CRONTAB", false);
}
Параллельно в административном разделе (Настройки → Настройки продукта → Настройки модулей → Главный модуль) отключают опцию «Автоматически проверять активность агентов и рассылку почты на хитах». После этого посетительские запросы перестают тянуть за собой агенты.
Вторая часть — сам вызов по расписанию. В crontab добавляют строку, которая раз в минуту запускает PHP-скрипт разбора событий. Исторически это bitrix/modules/main/tools/cron_events.php; на многих проектах используют собственную обёртку bitrix/php_interface/cron_events.php, которая проставляет флаг BX_CRONTAB и подключает ядро:
# Открыть crontab пользователя веб-сервера: crontab -e
# Запуск разбора агентов и почтовой очереди раз в минуту:
* * * * * /usr/bin/php -f /home/site/www/bitrix/php_interface/cron_events.php >/dev/null 2>&1
Сам скрипт-обёртка в простом виде выглядит так — он поднимает ядро в CLI-режиме и запускает штатный агентный/почтовый обработчик:
<?php
// bitrix/php_interface/cron_events.php
// Помечаем контекст как cron ДО подключения ядра:
$_SERVER["DOCUMENT_ROOT"] = realpath(__DIR__ . "/..");
define("NO_KEEP_STATISTIC", true);
define("NOT_CHECK_PERMISSIONS", true);
define("BX_CRONTAB", true); // ключевой флаг «это cron»
define("CHK_EVENT", true);
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_before.php");
// Разбор агентов и почтовой очереди штатным механизмом ядра
\CAgent::CheckAgents();
define("BX_CRONTAB_SUPPORT", true);
\CEvent::CheckEvents();
Перевод агентов на cron: пошагово
Собираем всё в понятную последовательность. Порядок важен: сначала настраиваем cron и убеждаемся, что он работает, и только потом отключаем хиты — иначе можно на время оставить проект вообще без выполнения регламентных задач.
- Проверьте, где и как запускаются агенты сейчас. Загляните в список агентов (админка) и в настройки главного модуля. Зафиксируйте текущий режим — чтобы было к чему откатиться.
- Подготовьте cron-скрипт. Создайте (или используйте штатный)
cron_events.php, который поднимает ядро с флагомBX_CRONTABи вызывает разбор агентов и почты. Проверьте путь к PHP-CLI на сервере. - Добавьте задание в crontab. Пропишите запуск скрипта раз в минуту от имени пользователя веб-сервера. Убедитесь, что у процесса есть права на файлы проекта.
- Убедитесь, что cron реально работает. Дождитесь пары минут и проверьте, что
NEXT_EXEC/LAST_EXECу агентов начали сдвигаться, а тестовое письмо из очереди ушло. Загляните в лог cron. - Только теперь отключите агенты на хитах. В
php_interfaceи настройках модуля выключите запуск агентов и почты на хитах. С этого момента вся ответственность на cron. - Настройте мониторинг «cron жив». Добавьте проверку, что регламентные задачи выполнялись недавно (например, свежесть
LAST_EXECключевого агента или внешний heartbeat). Сломавшийся cron не должен оставаться незамеченным. - Проверьте тяжёлые задачи под новыми лимитами. В CLI лимиты времени и памяти отличаются от веб. Убедитесь, что обмен с 1С и рассылки укладываются, при необходимости порционируйте.
Ключевая установка — не отключать хиты вслепую. Сначала докажите себе, что cron выполняет агенты, и только потом снимайте страховку в виде запуска на хитах. Иначе рискуете сутки не замечать, что рассылка и обмен встали.
Создание агента: CAgent::AddAgent
Свои агенты добавляют программно — обычно при установке модуля или разово из служебного скрипта. Базовый метод — CAgent::AddAgent(). Он принимает строку вызова, модуль, признак периодичности, интервал, дату первой проверки, активность, дату первого запуска и сортировку.
<?php
// Регистрируем агент, который раз в час синхронизирует остатки.
// Функция агента должна быть доступна на хите/в cron (например,
// через автозагрузку класса или подключение в init.php).
\CAgent::AddAgent(
"\\Vendor\\Shop\\StockAgent::run();", // строка вызова (с ; на конце)
"vendor.shop", // MODULE_ID
"N", // is_period: N — интервал от факта
3600, // AGENT_INTERVAL, сек (раз в час)
"", // дата первой проверки (пусто = сейчас)
"Y", // ACTIVE
"", // дата первого запуска (пусто = сейчас)
100 // SORT
);
Сама функция агента — это статический метод (или функция), который делает работу и возвращает строку своего вызова, чтобы ядро перепланировало его на следующий раз:
<?php
namespace Vendor\Shop;
class StockAgent
{
public static function run(): string
{
// 1. Основная работа агента (порционно и идемпотентно — см. ниже)
self::syncStocks();
// 2. Возвращаем строку СВОЕГО вызова — так ядро знает,
// как запланировать следующий запуск через интервал.
return "\\Vendor\\Shop\\StockAgent::run();";
}
private static function syncStocks(): void
{
// ... обмен остатками ...
}
}
Важные детали. Строка вызова должна заканчиваться точкой с запятой и содержать полностью квалифицированное имя (с namespace и экранированными слэшами). Чтобы не плодить дубли при повторной установке, перед добавлением проверяют, нет ли уже такого агента (по строке вызова и модулю), — иначе в списке появятся несколько одинаковых записей, и работа выполнится многократно. Удаляют агент через CAgent::RemoveAgent($name, $module); при удалении модуля его агенты убираются автоматически по MODULE_ID.
Есть и «объектная» надстройка над таблицей агентов в D7 — но под капотом это всё та же подсистема. Как устроено ядро D7 и ORM, через которые удобно писать сервисную логику агента, мы разбираем в статье про D7 и ORM в 1С-Битрикс.
Что возвращает функция агента
Возвращаемое значение функции агента — не формальность, а управляющий сигнал для ядра. Есть три сценария:
- Строка своего вызова (
return "Func();";) — стандартный периодический агент. Ядро понимает, что задачу нужно оставить и перепланировать через интервал. - Пустая строка (
return "";) — одноразовый агент. После выполнения ядро удаляет запись: задача была нужна один раз (например, разовая миграция данных, отложенная на ближайшую ночь). - Изменённая строка вызова — редкий приём, когда агент перепланирует сам себя на другую функцию или с другими параметрами. Используется осторожно.
Типичная ошибка новичка — не вернуть ничего из функции периодического агента. Тогда ядро трактует результат как пустую строку и удаляет агент после первого же выполнения: задача «отработала один раз и пропала». Если ваш агент должен повторяться, он обязан возвращать строку своего вызова.
<?php
function MyHourlyAgent()
{
// ... работа ...
// ОБЯЗАТЕЛЬНО вернуть свой вызов, иначе агент удалится после первого раза
return "MyHourlyAgent();";
}
function MyOneTimeAgent()
{
// ... разовая работа ночью ...
return ""; // пустая строка — агент самоудаляется, задача была разовой
}
return, ядро не получит строку перепланирования корректно, и агент может «застрять» — не сдвинуть NEXT_EXEC и запускаться снова и снова, либо, наоборот, перестать выполняться. Поэтому тело агента оборачивают в try/catch, логируют ошибку и всё равно возвращают строку своего вызова — чтобы сбой одного прогона не ломал расписание.
Идемпотентность агента
Регламентная задача обязана быть идемпотентной — то есть давать корректный результат даже при повторном или наложенном запуске. Это не теоретическая тонкость: агент реально может выполниться дважды (наложение cron и хитов, задержки, ручной перезапуск), и от того, как написана логика, зависит, получите вы дубли писем и двойное списание остатков — или нет.
Главный принцип идемпотентности — работать по состоянию, а не по времени. Плохой агент говорит «обработать всё, что появилось за последний час». Хороший — «обработать всё, что ещё не обработано», помечая каждую запись как обработанную. Тогда повторный запуск ничего не сломает: необработанных записей уже нет.
<?php
public static function run(): string
{
// Идемпотентно: берём только НЕобработанные записи по флагу,
// а не «все за интервал». Повторный запуск не создаст дублей.
$rows = QueueTable::getList([
'filter' => ['=PROCESSED' => 'N'],
'order' => ['ID' => 'ASC'],
'limit' => 50, // порция за один проход
])->fetchAll();
foreach ($rows as $row) {
self::process($row);
// Помечаем как обработанную СРАЗУ после успешной обработки
QueueTable::update($row['ID'], ['PROCESSED' => 'Y']);
}
return "\\Vendor\\Shop\\QueueAgent::run();";
}
Дополнительные приёмы идемпотентности: использовать во внешних системах ключ идемпотентности (уникальный идентификатор операции, по которому получатель отбрасывает повтор), проверять «уже существует ли результат» перед созданием (нет ли уже документа с таким номером), обновлять записи по первичному ключу, а не вставлять заново. Эти же принципы лежат в основе надёжных интеграций через очереди — подробно мы разбираем их в материале про API, очереди и надёжность интеграций в 1С-Битрикс.
Блокировки и повторный запуск
Идемпотентность защищает от повреждения данных при повторе, но есть смежная проблема — параллельное выполнение одного и того же агента двумя процессами одновременно. Это реально случается: cron запускается каждую минуту, а тяжёлый агент отрабатывает две минуты — второй запуск стартует, пока первый ещё не закончил. Или случайно активны и хиты, и cron.
Защита — блокировка: агент в начале пытается захватить эксклюзивный «замок» и, если не удаётся, тихо выходит, не делая работу повторно. Самый простой и надёжный способ — файловая блокировка через flock:
<?php
public static function run(): string
{
$self = "\\Vendor\\Shop\\HeavyAgent::run();";
$lockFile = sys_get_temp_dir() . '/vendor_shop_heavy.lock';
$fp = fopen($lockFile, 'c');
// Пытаемся захватить неблокирующую эксклюзивную блокировку.
// Не удалось — значит уже выполняется другой процесс, выходим.
if (!$fp || !flock($fp, LOCK_EX | LOCK_NB)) {
if ($fp) { fclose($fp); }
return $self; // просто перепланируем, работу не дублируем
}
try {
self::doHeavyWork(); // основная работа под замком
} catch (\Throwable $e) {
// логируем, но замок всё равно освободим в finally
\Bitrix\Main\Diag\Debug::writeToFile($e->getMessage(), '', 'heavy_agent.log');
} finally {
flock($fp, LOCK_UN);
fclose($fp);
}
return $self;
}
Альтернативы flock: блокировка на уровне СУБД (GET_LOCK в MySQL), «замок» в опции модуля или в отдельной таблице с проверкой TTL (чтобы зависший процесс не заблокировал задачу навсегда). У блокировки через опцию есть риск «мёртвого замка», если процесс упал, не сняв флаг, — поэтому всегда предусматривайте таймаут или снятие замка в finally. flock хорош тем, что ОС сама освобождает блокировку при завершении процесса.
Почтовые события и очередь писем
Отправка писем в Битриксе почти всегда идёт через очередь почтовых событий, а не мгновенно. Когда код инициирует почтовое событие (CEvent::Send), письмо не уходит сразу — оно кладётся в таблицу почтовых событий со статусом «к отправке». Фактическую отправку выполняет системный механизм, который порциями разбирает эту очередь. И этот разбор — тоже регламентная задача, завязанная на агентно-cron-подсистему.
Отсюда практические следствия:
- На хитах письма уходят «по трафику». Разбор очереди на посетительских запросах означает, что скорость рассылки зависит от посещаемости, а ночью письма могут «зависнуть» до утра.
- На cron рассылка стабильна. Скрипт
cron_events.phpрегулярно вызывает разбор очереди (CEvent::CheckEvents()), и письма уходят ровными порциями независимо от посетителей. - Порцию за проход ограничивают. Чтобы не упереться в лимиты SMTP-сервера и время выполнения, за один проход отправляют ограниченное число писем. Остальное уйдёт в следующие минуты.
Транзакционные письма (подтверждение заказа, смена статуса, восстановление пароля) через очередь работают отлично: важно, что они уйдут в течение минуты-двух. А вот массовые маркетинговые рассылки на десятки тысяч адресов через штатную очередь гнать не стоит — она не рассчитана на такой объём, забьёт очередь и может испортить репутацию домена. Их выносят в специализированный сервис рассылок с прогревом и обработкой отписок. Как связать магазин с внешними системами по расписанию и через события, мы показываем в статье про события и обработчики в 1С-Битрикс.
Обмен с 1С по расписанию
Обмен с 1С — одна из самых чувствительных к расписанию задач магазина. Товары, цены, остатки, статусы заказов должны приходить регулярно и вовремя. И здесь режим запуска агентов играет решающую роль.
Обмен бывает двух типов по инициатору:
- 1С инициирует обмен (пуш): 1С по своему расписанию подключается к сайту и передаёт данные через стандартный протокол обмена. Здесь расписание держит сторона 1С, а сайт лишь принимает данные. Агенты Битрикса тут второстепенны.
- Сайт инициирует обмен (пул): сайт по расписанию сам ходит в 1С или во внешний источник за данными. Вот здесь регламентная задача на стороне Битрикса — агент или cron-скрипт — критична.
Если обмен, инициируемый сайтом, повесить на агент на хитах, получаем классическую ловушку: ночью нет посетителей — обмен не идёт — утром остатки и цены устарели. Перевод на cron решает проблему: обмен выполняется по часам независимо от трафика. При этом сама задача обмена должна быть идемпотентной и порционной — падение посреди большого каталога не должно приводить к дублям или потере данных. Полный разбор построения обмена с учётом расписания и надёжности — в статье про интеграцию интернет-магазина с 1С.
Тяжёлые задачи: не в агенте на хите
Здесь кроется самая частая архитектурная ошибка. Разработчик пишет агент, который делает много работы — генерирует тысячи документов, обходит весь каталог, формирует большую выгрузку — и оставляет его на хитах. Дальше происходит одно из двух: либо агент выполняется в запросе посетителя и тот несколько секунд ждёт пустую страницу, либо агент упирается в лимит времени выполнения PHP (max_execution_time) и обрывается на середине, оставляя работу недоделанной, а данные — в противоречивом состоянии.
Тяжёлые фоновые задачи требуют другого подхода. Есть три уровня решения, от простого к правильному:
- Порционирование (chunking). Агент за один запуск обрабатывает не всё, а порцию (N записей), сохраняя позицию. Следующий запуск продолжает с сохранённого места. Так тяжёлая работа «размазывается» по многим коротким прогонам и никогда не упирается в лимит.
- Отдельный cron-скрипт. Задачу выносят из агента в самостоятельный CLI-скрипт, который cron запускает с нужными (увеличенными) лимитами. Он не связан с веб-настройками и не мешает посетителям.
- Очередь и воркер. Самый надёжный вариант для по-настоящему тяжёлых и важных задач: агент лишь ставит задание в очередь, а обрабатывает его отдельный процесс-воркер. Очередь даёт повторные попытки, контроль порядка, изоляцию сбоев и горизонтальное масштабирование.
Разница между «агент делает всё сам» и «агент ставит задание в очередь» — это разница между хрупкой и надёжной системой. В первом случае сбой роняет всю задачу; во втором — задание остаётся в очереди и будет повторено. Про то, как строить такие очереди и обеспечивать надёжность обмена, у нас есть отдельный подробный материал — API, очереди и надёжность интеграций.
Пул задач и параллельность
Когда агентов и фоновых задач становится много, встаёт вопрос порядка и параллельности их выполнения. По умолчанию агенты в Битриксе выполняются последовательно: за один проход CheckAgents() ядро перебирает назревшие агенты по порядку сортировки и выполняет их один за другим в одном процессе. Это важно понимать: тяжёлый агент задерживает те, что стоят после него.
Несколько практических следствий и приёмов:
- Сортировка (
SORT) задаёт порядок. Критичные и быстрые задачи ставьте раньше, чтобы они не ждали «тяжеловесов». Но помните: порядок не гарантирует времени — только очередь внутри прохода. - Общий лимит времени. Один проход разбора агентов ограничен по времени. Если суммарно агенты не укладываются, часть перенесётся на следующий запуск. Держите отдельные агенты короткими.
- Параллельность — через отдельные процессы. Если задачи независимы и их можно выполнять одновременно, не нагружайте один агентный проход — заведите отдельные cron-скрипты (каждый со своим расписанием) или очередь с несколькими воркерами. Тогда работа идёт параллельно и не «толпится» в одном потоке.
- Разделяйте «быстрое» и «медленное». Быстрые регламентные задачи (разбор очереди, мелкие пересчёты) оставляйте в общем агентном проходе; тяжёлые и долгие выносите в отдельные процессы, чтобы они не тормозили остальное.
Главный принцип: не всё, что делается по расписанию, должно жить в одном агентном проходе. Агенты хороши для лёгких регулярных задач. Для тяжёлого и параллельного — отдельные процессы и очереди с воркерами, которые масштабируются независимо.
Типы регламентных задач
Чтобы выбрать правильный механизм под конкретную задачу, полезно держать в голове карту типов. Не всё нужно делать агентом — иногда правильнее отдельный cron-скрипт или очередь.
| Тип задачи | Пример | Чем делать |
|---|---|---|
| Лёгкая регулярная | Разбор очереди писем, мелкие пересчёты | Агент (на cron) |
| Периодическая «примерно каждые N» | Обновление курсов, чистка временных файлов | Агент с интервалом |
| Строго по времени суток | Ночная выгрузка ровно в 3:00 | Отдельный cron-скрипт |
| Тяжёлая пакетная | Обмен большого каталога, генерация тысяч документов | Порционный агент или cron-скрипт |
| Тяжёлая и критичная | Массовая обработка заказов, интеграция с внешним API | Очередь + воркер |
| Разовая отложенная | Единичная миграция данных ночью | Одноразовый агент (return "") |
| Реакция на событие | Отправить письмо при смене статуса заказа | Обработчик события (не агент) |
Обратите внимание на последнюю строку: не всё делается по расписанию. Если задача — реакция на конкретное действие (создан заказ, изменён статус), это работа для обработчика события, а не для агента, который опрашивает состояние. Агент опрашивает («не появилось ли нового?»), событие реагирует («вот, появилось»). Смешивать эти подходы — частая причина лишней нагрузки и задержек.
Отладка: «агент не запускается»
«Агент не срабатывает» — одна из самых частых жалоб. Проблема почти всегда в одной из нескольких типовых точек, и отладка идёт по чёткому дереву проверок. Пройдите его сверху вниз:
- Агент вообще существует и активен? Откройте список агентов в админке. Проверьте, что запись есть,
ACTIVE='Y', а строка вызова указывает на существующую функцию/метод. - Какое у него
NEXT_EXEC? Если время следующего запуска в будущем — агент просто ещё не должен был выполниться. Если далеко в будущем без причины — возможно, кто-то его перепланировал или он «застрял» после ошибки. - Как запускаются агенты — хиты или cron? Если на хитах, а трафика нет (ночь, dev-сайт) — агент физически не дёргается. Зайдите на сайт и проверьте, сдвинулся ли
LAST_EXEC. Если на cron — переходите к следующему пункту. - Cron реально вызывает скрипт? Проверьте crontab, логи cron, права процесса. Запустите
cron_events.phpвручную из консоли и посмотрите, отработал ли он без ошибок. - Функция агента не падает? Временно залогируйте вход в функцию и ключевые шаги. Проверьте лог ошибок PHP: фатальная ошибка или исключение могут прерывать агент до перепланирования.
- Модуль агента установлен? Если
MODULE_IDуказывает на неустановленный/удалённый модуль или класс недоступен (нет автозагрузки, не подключён вinit.php) — вызов не состоится. - Нет ли блокировки-«мертвяка»? Если агент использует блокировку и предыдущий процесс упал, не сняв замок, новые запуски будут выходить сразу. Проверьте lock-файл/флаг.
Мониторинг выполнения
Главная опасность фоновых задач — их незаметность. Когда рассылка встала или обмен не прошёл, посетитель этого не видит: страница отдаётся как обычно. Проблему замечают, когда клиент звонит «где моё письмо» или менеджер видит устаревшие остатки. Поэтому регламентные задачи обязательно мониторят.
Что именно контролировать:
- Свежесть выполнения (heartbeat). У ключевых агентов проверяйте, что
LAST_EXECобновлялся недавно (например, не более N минут назад). Если время «замерло» — задачи встали. Это самый простой и мощный индикатор. - Размер очередей. Растущая очередь писем или заданий — признак, что разбор не поспевает или встал. Отслеживайте длину очереди и алертите при превышении порога.
- Ошибки в логах. Пишите в лог начало, конец и ошибки каждого важного агента. Всплеск ошибок или отсутствие записей «завершено» — сигнал проблемы.
- Внешний heartbeat/deadman. Пусть агент периодически «пингует» внешний сервис мониторинга. Если пинг не пришёл вовремя — сервис сам поднимет тревогу. Так вы узнаете о поломке cron, даже если весь сайт «лежит».
- Итоги обмена. Для обмена с 1С логируйте, сколько записей обработано за прогон. Нулевые прогоны там, где данные должны меняться, — повод разобраться.
Мониторинг превращает «тихую» аварию в громкую: вместо того чтобы узнать о вставшем обмене от рассерженного клиента, вы получаете сигнал и чините проблему заранее. Как выстроить логирование и наблюдаемость фоновых процессов на Битриксе в целом — тема статьи про логирование, отладку и мониторинг в 1С-Битрикс.
Частые ошибки
Регламентные задачи порождают свой набор типовых граблей. Зная их заранее, вы избежите большинства ночных сюрпризов.
- Оставить агенты на хитах в продакшене. Ночью и на низком трафике задачи не выполняются вовремя, а тяжёлые агенты тормозят посетителя. Переводите на cron.
- Отключить хиты, не проверив cron. Сначала убедитесь, что cron реально выполняет агенты, и только потом снимайте запуск на хитах — иначе регламентные задачи встанут незаметно.
- Не вернуть строку вызова из периодического агента. Пустой
returnзаставляет ядро удалить агент после первого выполнения — задача «отработала раз и пропала». - Тяжёлая работа в одном агенте на хите. Упирается в лимит времени и обрывается на середине. Порционируйте или выносите в отдельный процесс/очередь.
- Неидемпотентная задача. «Обработать всё за час» вместо «обработать необработанное» даёт дубли писем и двойные списания при повторе. Работайте по состоянию, а не по времени.
- Нет блокировки от параллельного запуска. Долгий агент при частом cron может наложиться сам на себя. Ставьте
flockили другой замок с таймаутом. - Дубли агентов при повторной установке.
AddAgentбез проверки существования плодит одинаковые записи, и работа выполняется многократно. Проверяйте перед добавлением. - Нет обработки ошибок в теле агента. Непойманное исключение ломает перепланирование и «застревает» агент. Оборачивайте в
try/catch, логируйте, всё равно возвращайте строку вызова. - Массовая рассылка через штатную очередь событий. Десятки тысяч писем забивают очередь и портят репутацию домена. Выносите в специализированный сервис рассылок.
- Никакого мониторинга. Про вставший обмен или рассылку узнают от клиентов. Мониторьте свежесть
LAST_EXECи размер очередей.
Чек-лист регламентных задач
Короткий список, по которому удобно свериться при настройке фоновых задач и на код-ревью:
- Агенты в продакшене запускаются на cron, а не на хитах; запуск на хитах отключён.
- Перед отключением хитов проверено, что cron реально выполняет агенты (сдвиг
LAST_EXEC). - Каждый периодический агент возвращает строку своего вызова; одноразовые — пустую строку.
- Тело агента обёрнуто в
try/catch, ошибки логируются, расписание не ломается при сбое. - Задачи идемпотентны: работают по состоянию (необработанное), а не «за период».
- Долгие агенты защищены блокировкой (
flock/иное) с таймаутом от «мёртвого замка». - Тяжёлые задачи порционированы или вынесены в отдельный cron-скрипт/очередь с воркером.
- Обмен с 1С, инициируемый сайтом, идёт по cron и не зависит от трафика.
- Рассылка писем идёт через очередь по cron; массовые рассылки — во внешнем сервисе.
AddAgentвызывается с проверкой на дубли; агенты привязаны к своему модулю.- Настроен мониторинг: свежесть выполнения, размер очередей, ошибки, внешний heartbeat.
Вывод
Агенты в 1С-Битрикс — это встроенный механизм периодических задач: запись в таблице с функцией, интервалом и временем следующего запуска, которую ядро выполняет по «толчку» извне. Ключевой выбор — чем давать этот толчок. Режим на хитах привязывает задачи к трафику и выполняет их в запросах посетителей: ненадёжно ночью и вредно для скорости страниц. Режим на cron запускает агенты по расписанию операционной системы, независимо от посетителей и в отдельном процессе, — это стандарт для любого боевого проекта. Перевод на cron — это отключение хитов в php_interface плюс регулярный вызов cron_events.php из crontab, обязательно с проверкой, что cron жив.
Надёжные фоновые задачи держатся на трёх опорах. Идемпотентность — повторный запуск безопасен, потому что задача работает по состоянию, а не по времени. Блокировки — параллельное выполнение невозможно, дублей нет. Правильная архитектура тяжёлого — большую работу не гонят в агенте на хите, а порционируют или выносят в отдельный процесс и очередь с воркером. Плюс обязательный мониторинг, который превращает «тихую» аварию в громкий алерт. Если нужно настроить всё это под ваш проект — мы делаем это в рамках поддержки и highload-разработки на 1С-Битрикс. Смежные материалы — API, очереди и надёжность интеграций, события и обработчики, интеграция магазина с 1С и вся рубрика «Разработка и архитектура».