Ночной обмен прошёл успешно, лог чистый, товары обновились. А выгрузка на маркетплейс, которая должна была уйти сразу после обмена, ушла в девять утра — когда на сайт зашёл первый посетитель.
Это не баг обмена. Это следствие того, как в Битриксе устроен запуск фоновых задач, и оно бьёт по всем интеграциям, которые работают ночью. Разработчик, привыкший к нормальному 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
// /bitrix/php_interface/dbconn.php
define("BX_CRONTAB_SUPPORT", true);Сверьте имя константы с документацией своей версии. Битрикс использует несколько похожих констант вокруг cron, и они менялись между версиями. Неправильно названная константа не вызовет ошибки — она просто ничего не сделает, а вы будете считать, что cron включён. Проверять нужно по факту: агент, поставленный на ближайшую минуту, должен выполниться без единого визита на сайт.
Шаг 2: выключить запуск на хите
Настройки → Настройки продукта → Настройки модулей → Главный модуль → снять галочку «Автоматически проверять выполнение агентов».
Без этого шага агенты будут выполняться и по cron, и на хите одновременно. Работать это будет, но смысл теряется: тормоза у первого посетителя никуда не денутся.
Шаг 3: добавить задание в crontab
# crontab -e под пользователем, от которого работает PHP
*/1 * * * * /usr/bin/php -f /home/site/www/bitrix/modules/main/tools/cron_events.php > /dev/null 2>&1Минутный интервал — разумный дефолт. Пятиминутный тоже годится, если ничего не требует реакции быстрее.
Проверка, что всё заработало
Не «галочки стоят», а «агент выполнился без посетителей»:
<?php
// Разовый скрипт: ставим агента на ближайшую минуту
CAgent::AddAgent(
'file_put_contents($_SERVER["DOCUMENT_ROOT"]."/upload/cron_test.log", date("c")."\n", FILE_APPEND);',
'main',
'N',
60
);# Ждём минуту, не открывая сайт, и смотрим
cat /home/site/www/upload/cron_test.logФайл появился без единого визита — cron работает. Не появился — вернитесь к шагу 1, дело в константе.
Расписание: что и с какой частотой
Разведение файлов по разным интервалам — самая недооценённая настройка обмена. Она делается один раз и снимает большую часть нагрузки.
| Что | Файл | Разумный интервал | Почему |
|---|---|---|---|
| Структура и товары | import.xml | 1–2 раза в сутки, ночью | Тяжёлый, меняется редко |
| Цены и остатки | offers.xml | 15–30 минут | Лёгкий, расхождение со складом стоит дорого |
| Заказы на сторону 1С | type=sale | 5–15 минут | Менеджер ждёт заказ в 1С, а не через час |
Логика простая: часто гоняем то, что часто меняется и мало весит. Полный каталог в пятнадцатиминутном цикле — классическая ошибка, у которой два следствия сразу. Сервер занят постоянно, а обмен перестаёт успевать закончиться до следующего запуска, и вы получаете параллельные обмены — про них ниже.
Ночь — понятие относительное. Если у магазина есть покупатели из Владивостока и Калининграда, «тихих часов» по московскому времени у него нет. Прежде чем ставить тяжёлый обмен на три часа ночи, посмотрите распределение трафика по часам в метрике: минимум может оказаться в пять утра или в обеденное время воскресенья.
Параллельные обмены и блокировка
Второй обмен, стартовавший до завершения первого, — не гипотетическая ситуация. Он случается ровно тогда, когда интервал в 1С меньше реального времени обмена, а такое бывает после каждого сезонного роста каталога.
Два процесса пишут одни и те же элементы. Итог зависит от того, чей запрос выполнился последним: часть цен окажется из старой выгрузки, часть из новой, а таблицы будут блокировать друг друга.
Важная тонкость: обмен — это не один запрос, а цепочка запросов, растянутая на минуты. Поэтому блокировка внутри одного HTTP-запроса бесполезна — она снимется в конце шага, а следующий шаг придёт отдельным запросом. Блокировать нужно весь цикл.
Рабочий приём — именованная блокировка в MySQL, которая живёт в рамках соединения, плюс явное снятие по завершении:
<?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 за нужный период:
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-запросов, и блокировка внутри одного запроса ничего не даёт.