Обмен с 1С по расписанию: cron вместо агентов

Почему обмен ночью проходит, а обработка после него не запускается, как развести import.xml и offers.xml по разным интервалам и чем закрыть параллельный запуск.

Дмитрий Мещеряков
Дмитрий Мещеряков
📅 5 октября 2026 г.📖 8 мин чтения

Ночной обмен прошёл успешно, лог чистый, товары обновились. А выгрузка на маркетплейс, которая должна была уйти сразу после обмена, ушла в девять утра — когда на сайт зашёл первый посетитель.

Это не баг обмена. Это следствие того, как в Битриксе устроен запуск фоновых задач, и оно бьёт по всем интеграциям, которые работают ночью. Разработчик, привыкший к нормальному cron, такую ловушку не ждёт: задача поставлена, обработчик написан, всё правильно — просто некому нажать на кнопку.

Разберём, что здесь на самом деле выполняется по расписанию, а что только притворяется.

Кто кем управляет: обмен инициирует 1С

Первое, что нужно принять: сайт обмен не запускает. У Битрикса нет и не может быть настройки «обмениваться с 1С каждые 15 минут». Точка входа /bitrix/admin/1c_exchange.php — пассивная, она умеет только отвечать. Инициатор всегда на стороне 1С: регламентное задание в конфигурации ходит на сайт и отдаёт команды, механика которых разобрана в статье про протокол обмена по CommerceML.

Отсюда простое разделение зон ответственности, которое почему-то приходится проговаривать почти на каждом проекте:

Что настраиваетсяГде
Частота обмена, состав выгрузки, отбор товаров1С, регламентное задание
Разбор файлов, создание элементов, ценыБитрикс, настройки обмена
Всё, что должно произойти после обменаБитрикс, агенты или cron

Третья строка и есть источник проблем. Разработчик пишет обработчик «после импорта поставить задачу в очередь», ставит задачу агентом — и она повисает до утра.

Почему агенты ночью не работают

Агент — это функция, которая должна выполниться в заданное время. Механизм подробно разобран в статье про агенты Битрикса, здесь важна одна деталь его реализации.

По умолчанию агенты работают на хите. Проверка «не пора ли что-нибудь выполнить» встроена в обработку обычного запроса посетителя: человек открывает страницу, Битрикс попутно смотрит очередь агентов и запускает просроченные.

Следствия видны сразу, стоит их выписать:

  • Нет посетителей — нет агентов. Ночью сайт стоит, очередь не разбирается.
  • Запросы 1С не считаются. Обмен идёт в административную часть, где агентов не крутят.
  • Первый утренний посетитель платит за всех. Он получает страницу, во время генерации которой выполняется вся накопившаяся за ночь очередь.

Последний пункт — отдельный сорт неприятности. Человек заходит на сайт в 9:03, попадает на хит, где отрабатывает выгрузка 12 000 товаров на маркетплейс, и ждёт ответа сорок секунд. В метрике это выглядит как случайный выброс TTFB, воспроизвести который невозможно.

⚠️ Важно

Проверьте это на своём проекте прямо сейчас. Если в dbconn.php нет константы поддержки cron, а в настройках главного модуля стоит галочка «Автоматически проверять выполнение агентов» — у вас агенты на хите, и вся ночная автоматика работает не тогда, когда вы думаете. Симптом со стороны бизнеса звучит как «цены на маркетплейсе обновляются с задержкой в полдня», и связать это с обменом никто не догадывается.

Перевод агентов на cron

Настройка состоит из трёх шагов, и все три обязательны — пропуск любого оставляет систему в промежуточном состоянии.

Шаг 1: включить поддержку cron

php
<?php
// /bitrix/php_interface/dbconn.php

define("BX_CRONTAB_SUPPORT", true);
💡 Совет

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

Шаг 2: выключить запуск на хите

Настройки → Настройки продукта → Настройки модулей → Главный модуль → снять галочку «Автоматически проверять выполнение агентов».

Без этого шага агенты будут выполняться и по cron, и на хите одновременно. Работать это будет, но смысл теряется: тормоза у первого посетителя никуда не денутся.

Шаг 3: добавить задание в crontab

bash
# crontab -e под пользователем, от которого работает PHP
*/1 * * * * /usr/bin/php -f /home/site/www/bitrix/modules/main/tools/cron_events.php > /dev/null 2>&1

Минутный интервал — разумный дефолт. Пятиминутный тоже годится, если ничего не требует реакции быстрее.

Проверка, что всё заработало

Не «галочки стоят», а «агент выполнился без посетителей»:

php
<?php
// Разовый скрипт: ставим агента на ближайшую минуту
CAgent::AddAgent(
    'file_put_contents($_SERVER["DOCUMENT_ROOT"]."/upload/cron_test.log", date("c")."\n", FILE_APPEND);',
    'main',
    'N',
    60
);
bash
# Ждём минуту, не открывая сайт, и смотрим
cat /home/site/www/upload/cron_test.log

Файл появился без единого визита — cron работает. Не появился — вернитесь к шагу 1, дело в константе.

Расписание: что и с какой частотой

Разведение файлов по разным интервалам — самая недооценённая настройка обмена. Она делается один раз и снимает большую часть нагрузки.

ЧтоФайлРазумный интервалПочему
Структура и товарыimport.xml1–2 раза в сутки, ночьюТяжёлый, меняется редко
Цены и остаткиoffers.xml15–30 минутЛёгкий, расхождение со складом стоит дорого
Заказы на сторону 1Сtype=sale5–15 минутМенеджер ждёт заказ в 1С, а не через час

Логика простая: часто гоняем то, что часто меняется и мало весит. Полный каталог в пятнадцатиминутном цикле — классическая ошибка, у которой два следствия сразу. Сервер занят постоянно, а обмен перестаёт успевать закончиться до следующего запуска, и вы получаете параллельные обмены — про них ниже.

💡 Совет

Ночь — понятие относительное. Если у магазина есть покупатели из Владивостока и Калининграда, «тихих часов» по московскому времени у него нет. Прежде чем ставить тяжёлый обмен на три часа ночи, посмотрите распределение трафика по часам в метрике: минимум может оказаться в пять утра или в обеденное время воскресенья.

Параллельные обмены и блокировка

Второй обмен, стартовавший до завершения первого, — не гипотетическая ситуация. Он случается ровно тогда, когда интервал в 1С меньше реального времени обмена, а такое бывает после каждого сезонного роста каталога.

Два процесса пишут одни и те же элементы. Итог зависит от того, чей запрос выполнился последним: часть цен окажется из старой выгрузки, часть из новой, а таблицы будут блокировать друг друга.

Важная тонкость: обмен — это не один запрос, а цепочка запросов, растянутая на минуты. Поэтому блокировка внутри одного HTTP-запроса бесполезна — она снимется в конце шага, а следующий шаг придёт отдельным запросом. Блокировать нужно весь цикл.

Рабочий приём — именованная блокировка в MySQL, которая живёт в рамках соединения, плюс явное снятие по завершении:

php
<?php
// /bitrix/php_interface/init.php
use Bitrix\Main\Application;
use Bitrix\Main\EventManager;

/**
 * Обмен состоит из множества HTTP-запросов, поэтому признак «идёт обмен»
 * держим не в памяти процесса, а в общем хранилище — файле или таблице.
 */
function exchangeLockFile(): string
{
    return $_SERVER['DOCUMENT_ROOT'] . '/upload/1c_catalog/.exchange.lock';
}

function exchangeIsRunning(int $ttlSeconds = 3600): bool
{
    $file = exchangeLockFile();
    if (!file_exists($file)) {
        return false;
    }
    // Просроченная метка означает упавший обмен, а не идущий:
    // без TTL один аварийный обмен заблокирует все следующие навсегда
    if (time() - (int)filemtime($file) > $ttlSeconds) {
        unlink($file);
        return false;
    }
    return true;
}

Дальше метка ставится в начале импорта и снимается по его завершении. Ключевая деталь здесь — TTL. Блокировка без срока жизни превращает одно падение обмена в остановку всех последующих, и разбираться с этим придётся руками в выходные.

⚠️ Важно

Снятие блокировки не должно зависеть от успешного завершения. Обмен падает по таймауту, по памяти, по ошибке в XML — и если снятие метки написано только в ветке успеха, она останется висеть. Поэтому TTL обязателен даже при аккуратно написанном снятии: он превращает вечную блокировку во временную.

Что проверить, когда «обмен по расписанию не работает»

Формулировка почти всегда означает одно из четырёх, и проверяются они по порядку.

1С вообще приходила? Смотрим лог веб-сервера — запросы к 1c_exchange.php за нужный период:

bash
grep "1c_exchange" /var/log/nginx/access.log | tail -20

Запросов нет — вопрос к регламентному заданию в 1С, сайт ни при чём. Метод разбора логов разобран в статье про анализ логов nginx и Apache.

Обмен дошёл до конца? В логе должна быть цепочка checkauth → init → file → серия import. Оборвалась на середине — смотрим лимиты PHP.

Файлы приехали? Смотрим дату изменения в /upload/1c_catalog/. Файл вчерашний — 1С прислала старые данные.

Обработка после обмена запустилась? Вот здесь и всплывают агенты на хите. Если товары обновились, а сопутствующая работа — нет, проверяйте cron первым делом.

Итоги

Расписание обмена живёт в 1С. Сайт — отвечающая сторона, и все попытки «настроить расписание в Битриксе» упираются в это.

Агенты на хите ломают всю ночную автоматику. Не частично, а целиком: без посетителей очередь не разбирается вообще. Перевод на cron — обязательная настройка для любого проекта с ночным обменом, а не оптимизация.

Проверяйте cron по факту выполнения, а не по наличию галочек. Тестовый агент, записавший строку в файл без единого визита на сайт, — единственное надёжное доказательство.

Разводите import.xml и offers.xml по разным интервалам. Это бесплатно и снимает больше нагрузки, чем любая последующая оптимизация запросов.

Блокировка нужна на весь цикл обмена и обязательно с TTL. Без TTL вы меняете редкую проблему параллельных обменов на регулярную проблему намертво вставшего обмена.

И наблюдение, ради которого стоит перечитать раздел про агенты: жалоба «данные на сайте обновляются с задержкой в полдня» почти никогда не про обмен. Обмен отработал ночью, как и должен. Не отработало то, что вы повесили после него — и не отработает, пока на сайт не зайдёт живой человек.

Частые вопросы

Как настроить обмен с 1С по расписанию?

Расписание задаётся в 1С — регламентным заданием в конфигурации. Сайт обмен не инициирует и запустить его сам не может: он только отвечает на запросы к /bitrix/admin/1c_exchange.php. На стороне Битрикса настраивают не расписание обмена, а расписание сопутствующей работы: агентов, пересчётов, выгрузок на маркетплейсы.

Почему агенты не выполняются во время ночного обмена?

Потому что по умолчанию агенты запускаются «на хите» — их проверка встроена в обработку обычного запроса посетителя. Ночью посетителей нет, а запросы 1С к 1c_exchange.php идут в административной части и агентов не крутят. Отложенная задача, поставленная в 3 часа ночи, выполнится при первом визите утром — задержка 6–8 часов.

Как перевести агенты Битрикса на cron?

В /bitrix/php_interface/dbconn.php добавляется константа поддержки cron, в настройках главного модуля снимается галочка «Автоматически проверять выполнение агентов», а в crontab добавляется вызов /bitrix/modules/main/tools/cron_events.php с интервалом 1–5 минут. Точное имя константы стоит сверить с документацией своей версии Битрикса.

Как часто запускать обмен с 1С?

Полный каталог (import.xml) — один-два раза в сутки в часы минимальной нагрузки, потому что он тяжёлый и меняется редко. Цены и остатки (offers.xml) — каждые 15–30 минут, потому что расхождение остатков со складом стоит дороже, чем нагрузка от лёгкого файла. Ставить полный каталог на 15-минутный интервал — верный способ получить постоянно занятый сервер и обмен, который не успевает закончиться до следующего запуска.

Что будет, если два обмена запустятся одновременно?

Два процесса начнут писать одни и те же товары, и результат зависит от порядка выполнения запросов. Типичный итог — часть цен от старой выгрузки, часть от новой, плюс взаимные блокировки таблиц и упавший по таймауту шаг. Защищаться нужно блокировкой на весь цикл обмена, потому что обмен состоит из множества HTTP-запросов, и блокировка внутри одного запроса ничего не даёт.