Проблема медленного расчёта доставки в Битрикс
Расчёт стоимости и сроков доставки — один из наиболее ресурсоёмких этапов оформления заказа в Битрикс. Каждая служба доставки делает внешний HTTP-запрос к своему API: СДЭК, Boxberry, Почта России, DHL — все они отвечают с разной скоростью. На странице корзины или оформления заказа Битрикс запрашивает расчёт сразу у всех подключённых служб, и если хотя бы одна из них отвечает медленно или вовсе недоступна, пользователь видит крутящийся прелоадер по несколько секунд.
Чтобы оптимизировать этот процесс, нужно сначала измерить: какая именно служба тормозит и насколько. Без данных профилирования любые попытки ускорить расчёт — это работа вслепую. В этой статье показан простой способ логировать время расчёта каждой службы доставки через стандартный EventManager Битрикса.
Почему важно профилировать службы доставки
Практика показывает, что 80% проблем с медленным расчётом доставки приходится на 1-2 службы из всех подключённых. Часто это:
- Служба с нестабильным API, которая периодически не отвечает по 10-15 секунд.
- Устаревший модуль с неоптимальным кодом, делающий несколько запросов вместо одного.
- Служба, которую магазин перестал использовать, но не отключил — она продолжает тормозить всех остальных.
Профилирование позволяет за несколько минут получить конкретные цифры по каждой службе и принять обоснованное решение: оптимизировать, отключить или заменить.
Где разместить код
Код обработчика событий размещается в файле /bitrix/php_interface/init.php. Этот файл подключается Битриксом при каждом запросе раньше любого другого кода сайта, что делает его идеальным местом для глобальных обработчиков событий. Если файл не существует — создайте его, Битрикс автоматически подхватит его при следующем запросе.
Код профилировщика
Добавьте следующий код в init.php. Лог будет записываться в файл /delivery_time_log.txt в корне сайта:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
use BitrixMainEvent; use BitrixMainEventResult; use BitrixMainEventManager; use BitrixMainDiagDebug; use BitrixSaleShipment; EventManager::getInstance()->addEventHandler("sale", "onSaleDeliveryServiceCalculate", function(Event $event) { $shipment = $event->getParameter('SHIPMENT'); if (!$shipment instanceof Shipment) { return; // если это не Shipment — выходим } $delivery = $shipment->getDelivery(); $id = $delivery->getId() ?: spl_object_hash($shipment); static $timers = []; static $logged = []; if (!isset($timers[$id])) { $timers[$id] = microtime(true); } else { $timeSpent = round(microtime(true) - $timers[$id], 4); // Логируем только один раз для каждого Shipment if (!isset($logged[$id])) { Debug::writeToFile([ 'DATE' => date("Y-m-d H:i:s"), 'DELIVERY' => $shipment->getDeliveryName(), 'TIME' => $timeSpent ], "", "/delivery_time_log.txt"); $logged[$id] = true; } unset($timers[$id]); } }); |
Объяснение кода
EventManager и событие onSaleDeliveryServiceCalculate
EventManager::getInstance()->addEventHandler() — стандартный способ подписаться на событие Битрикса. Первый аргумент — модуль (sale), второй — имя события (onSaleDeliveryServiceCalculate). Это событие вызывается дважды для каждой службы: перед началом расчёта и после его завершения. Именно это позволяет измерить время.
Микросекундный таймер
microtime(true) возвращает текущее время в секундах с микросекундной точностью в виде числа с плавающей точкой. При первом вызове события для конкретной службы (первый вызов) — фиксируем время старта в массиве $timers. При втором вызове — вычисляем разницу и получаем время расчёта в секундах с точностью до четырёх знаков после запятой.
Статические переменные
Массивы $timers и $logged объявлены как static внутри замыкания. Это означает, что они сохраняют своё значение между вызовами одного и того же обработчика в рамках одного HTTP-запроса. Без static каждый вызов события начинал бы с пустыми массивами.
Идентификация службы доставки
$delivery->getId() возвращает ID службы доставки из базы данных. Если по какой-то причине ID недоступен, используется spl_object_hash($shipment) — уникальный хеш объекта, гарантированно отличающийся для разных отправлений.
Debug::writeToFile()
Встроенный метод Битрикса для записи в файл. Принимает массив данных, метку и путь к файлу. Записи добавляются в конец файла, не перезаписывая предыдущие.
Как читать логи
После нескольких оформлений заказа откройте файл /delivery_time_log.txt в корне сайта. Каждая запись содержит дату, название службы доставки и время расчёта в секундах:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
Array ( [DATE] => 2024-01-15 14:32:01 [DELIVERY] => СДЭК [TIME] => 0.4521 ) Array ( [DATE] => 2024-01-15 14:32:04 [DELIVERY] => Почта России [TIME] => 3.1203 ) |
Время меньше 1 секунды — норма. Время 2-3 секунды — повод обратить внимание. Время свыше 5 секунд — критическая проблема, требующая немедленного решения.
Что делать, если нашли медленную службу
- Временно отключите службу в настройках магазина (Магазин → Настройки → Службы доставки) и проверьте, ускорился ли расчёт.
- Обновите модуль службы доставки — разработчики часто выпускают исправления производительности.
- Проверьте доступность API службы с вашего сервера:
curl -w "%{time_total}" https://api.cdek.ru/. - Настройте таймаут в коде модуля — большинство служб позволяют задать максимальное время ожидания ответа от API.
- Переведите расчёт в асинхронный режим — Битрикс поддерживает AJAX-расчёт доставки, при котором страница загружается немедленно, а доставка пересчитывается в фоне.
Альтернативные инструменты профилирования в Битрикс
Помимо описанного метода, для профилирования производительности в Битрикс доступны следующие инструменты:
- Панель отладки Битрикс — включается через параметр
BX_DEBUGвdbconn.php, показывает время выполнения запросов и событий прямо на странице. - BitrixMainDiagDebug::startTimeLabel() / endTimeLabel() — встроенные методы для замера времени отдельных участков кода с выводом в стандартный лог Битрикса.
- XHProf / Tideways — профилировщики PHP-уровня, дающие полную картину вызовов функций с временем выполнения каждой.
Итог
Описанный обработчик события — минимальный и ненавязчивый инструмент профилирования. Он не влияет на производительность самого расчёта и не изменяет логику работы магазина. Добавьте его на несколько дней, соберите статистику, найдите проблемную службу и устраните причину — после этого код можно удалить из init.php.
