Где правильно разместить HTTP-запрос при отправке данных документа: в обработке проведения или через план обмена?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке интеграций между 1С и внешними системами (другими базами 1С, веб-сайтами или ERP-системами) часто возникает вопрос: в какой момент и в каком событии кода лучше всего инициировать отправку данных? На первый взгляд, кажется логичным отправить HTTP-запрос в момент проведения документа, когда данные уже сформированы и проверены. Однако, как мы разберем далее, такой подход таит в себе серьезные архитектурные риски. Проанализируем ситуацию и выясним, как реализовать гарантированную доставку данных без потери производительности системы.

Проблема синхронной отправки в транзакции

Рассмотрим ситуацию, когда программист помещает вызов HTTP-запроса в процедуру ОбработкаПроведения модуля объекта. В 1С это событие всегда выполняется внутри неявной транзакции базы данных. Это означает, что пока код внутри процедуры не завершится, транзакция остается открытой, а соответствующие таблицы базы данных заблокированы для других пользователей.

Основные риски этого подхода:

  1. Блокировки и простои: Сетевой запрос к внешнему ресурсу может занять значительное время (от долей секунды до нескольких минут при плохом соединении). Все это время транзакция будет «держать» таблицы. В высоконагруженной системе это приведет к тому, что другие пользователи не смогут записывать документы того же типа или даже формировать отчеты.
  2. Откат транзакции: Представим, что HTTP-запрос прошел успешно и внешняя система приняла данные. Но сразу после этого в коде 1С возникла ошибка (например, не хватило остатков на складе или сработала проверка в подписке на событие). Транзакция в 1С откатится, документ не будет проведен, но внешняя система уже получила данные. Мы получаем рассинхронизацию баз.
  3. Зависимость от связи: Если внешний сервер недоступен, пользователь не сможет провести документ в своей базе. Система выдаст ошибку, и оперативная работа встанет из-за проблем с интеграцией.

Проанализируем пример кода, который часто пытаются использовать новички, и поймем, почему он опасен:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    // Выполнение движений документа...
    
    // ОПАСНО: HTTP-запрос внутри транзакции
    Результат = ИнтеграцияССервером.ОтправитьДанные(ЭтотОбъект);
    Если Не Результат Тогда
        // Если сервер не ответил, документ не проведется. Это плохо.
        Отказ = Истина; 
    КонецЕсли;
КонецПроцедуры

Рекомендуемое решение: Использование Планов обмена

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

Разберем этот механизм по шагам:

Шаг 1. Регистрация изменений

В конфигураторе создадим новый объект ПланОбмена. В составе плана укажем документы, которые необходимо передавать. При записи документа система автоматически (или программно) зафиксирует факт изменения в специальной таблице изменений этого плана обмена. Это действие выполняется мгновенно и не требует сетевых запросов.

Проанализируем, как программно зарегистрировать изменения в модуле объекта или через подписку на событие:


Процедура ПриЗаписи(Отказ)
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    // Регистрируем изменения для конкретного узла обмена
    ПланыОбмена.ЗарегистрироватьИзменения(УзелИнтеграции, ЭтотОбъект.Ссылка);
КонецПроцедуры

Шаг 2. Создание регламентного задания

Теперь создадим РегламентноеЗадание, которое будет запускаться раз в минуту (или с другой необходимой частотой). В модуле этого задания мы будем обходить таблицу регистраций и отправлять данные.

Рассмотрим пример кода для обработки очереди отправки:


Процедура ВыполнитьОтправкуДанных() Экспорт
    Узел = ПланыОбмена.Интеграция.НайтиПоНаименованию("ВнешняяСистема");
    
    ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(Узел, 100); // Берем порциями по 100
    Пока ВыборкаИзменений.Следующий() Цикл
        ОбъектДанных = ВыборкаИзменений.НомерСообщения;
        
        Попытка
            Ответ = ОтправитьПоHTTP(ВыборкаИзменений.Данные);
            Если Ответ.КодСостояния = 200 Тогда
                // Если успешно, удаляем регистрацию, чтобы не отправлять повторно
                ПланыОбмена.УдалитьРегистрациюИзменений(Узел, ВыборкаИзменений.Данные);
            КонецЕсли;
        Исключение
            // Записываем ошибку в журнал регистрации и пробуем в следующий раз
            ЗаписатьОшибкуВЖурнал(ОписаниеОшибки());
        КонецПопытки;
    КонецЦикла;
КонецПроцедуры

Преимущества асинхронного подхода

Использование связки "План обмена + Регламентное задание" дает нам следующие выгоды:

Альтернатива: Фоновые задания для "мгновенной" отправки

Если бизнес-требования диктуют необходимость отправить данные немедленно (в течение секунд), но мы все еще хотим избежать блокировок в транзакции, можно использовать фоновые задания. Рассмотрим, как это реализовать.

В событии ПриЗаписи (после успешного завершения транзакции) или в событии формы ПослеЗаписи можно запустить фоновый процесс, который выполнит HTTP-запрос в отдельном сеансе на сервере. Важно передавать в задание Ссылку на документ, а не сам Объект, так как объект в другом сеансе будет недоступен.

Посмотрим на пример реализации запуска фонового задания:


&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
    // Запускаем фоновую отправку, чтобы не заставлять пользователя ждать ответа сервера
    НаименованиеЗадания = "Отправка документа " + ТекущийОбъект.Ссылка;
    ПараметрыЗадания = Новый Массив;
    ПараметрыЗадания.Добавить(ТекущийОбъект.Ссылка);
    
    ФоновыеЗадания.Выполнить("ОбменДаннымиСервер.ОтправитьДокументФоном", ПараметрыЗадания, , НаименованиеЗадания);
КонецПроцедуры

Важные аспекты реализации HTTP-запросов

При написании кода самой отправки через HTTPСоединение, не забывайте про настройки таймаутов. По умолчанию таймаут может быть очень длительным. Мы рекомендуем устанавливать разумные пределы, например, 10–20 секунд.

Также следует учитывать идемпотентность на стороне приемника. Это свойство системы, при котором повторный запрос с теми же данными не приводит к созданию дублей. Поскольку в случае сетевых сбоев 1С может отправить один и тот же документ дважды (например, если ответ от сервера пришел с ошибкой, хотя данные были успешно записаны), принимающая сторона должна уметь распознавать повторы по ГУИДу (UUID) документа.

Использование Библиотеки стандартных подсистем (БСП)

Если вы работаете в современной конфигурации (УТ 11, ERP, БП 3.0), обязательно задействуйте возможности БСП. Подсистема «Очередь заданий» или механизмы «Обмена данными» уже содержат в себе все необходимые инструменты для регистрации, логирования и повтора попыток отправки сообщений. Это позволит вам сократить объем написанного кода и повысить надежность интеграции.

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

← На главную