При разработке интеграций между 1С и внешними системами (другими базами 1С, веб-сайтами или ERP-системами) часто возникает вопрос: в какой момент и в каком событии кода лучше всего инициировать отправку данных? На первый взгляд, кажется логичным отправить HTTP-запрос в момент проведения документа, когда данные уже сформированы и проверены. Однако, как мы разберем далее, такой подход таит в себе серьезные архитектурные риски. Проанализируем ситуацию и выясним, как реализовать гарантированную доставку данных без потери производительности системы.
Рассмотрим ситуацию, когда программист помещает вызов HTTP-запроса в процедуру ОбработкаПроведения модуля объекта. В 1С это событие всегда выполняется внутри неявной транзакции базы данных. Это означает, что пока код внутри процедуры не завершится, транзакция остается открытой, а соответствующие таблицы базы данных заблокированы для других пользователей.
Основные риски этого подхода:
Проанализируем пример кода, который часто пытаются использовать новички, и поймем, почему он опасен:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Выполнение движений документа...
// ОПАСНО: HTTP-запрос внутри транзакции
Результат = ИнтеграцияССервером.ОтправитьДанные(ЭтотОбъект);
Если Не Результат Тогда
// Если сервер не ответил, документ не проведется. Это плохо.
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Для создания надежного и отказоустойчивого механизма обмена данными мы рекомендуем использовать асинхронный подход с применением Планов обмена. В этой схеме отправка происходит не в момент записи документа, а в отдельном фоновом процессе.
Разберем этот механизм по шагам:
В конфигураторе создадим новый объект ПланОбмена. В составе плана укажем документы, которые необходимо передавать. При записи документа система автоматически (или программно) зафиксирует факт изменения в специальной таблице изменений этого плана обмена. Это действие выполняется мгновенно и не требует сетевых запросов.
Проанализируем, как программно зарегистрировать изменения в модуле объекта или через подписку на событие:
Процедура ПриЗаписи(Отказ)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Регистрируем изменения для конкретного узла обмена
ПланыОбмена.ЗарегистрироватьИзменения(УзелИнтеграции, ЭтотОбъект.Ссылка);
КонецПроцедуры
Теперь создадим РегламентноеЗадание, которое будет запускаться раз в минуту (или с другой необходимой частотой). В модуле этого задания мы будем обходить таблицу регистраций и отправлять данные.
Рассмотрим пример кода для обработки очереди отправки:
Процедура ВыполнитьОтправкуДанных() Экспорт
Узел = ПланыОбмена.Интеграция.НайтиПоНаименованию("ВнешняяСистема");
ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(Узел, 100); // Берем порциями по 100
Пока ВыборкаИзменений.Следующий() Цикл
ОбъектДанных = ВыборкаИзменений.НомерСообщения;
Попытка
Ответ = ОтправитьПоHTTP(ВыборкаИзменений.Данные);
Если Ответ.КодСостояния = 200 Тогда
// Если успешно, удаляем регистрацию, чтобы не отправлять повторно
ПланыОбмена.УдалитьРегистрациюИзменений(Узел, ВыборкаИзменений.Данные);
КонецЕсли;
Исключение
// Записываем ошибку в журнал регистрации и пробуем в следующий раз
ЗаписатьОшибкуВЖурнал(ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Использование связки "План обмена + Регламентное задание" дает нам следующие выгоды:
Если бизнес-требования диктуют необходимость отправить данные немедленно (в течение секунд), но мы все еще хотим избежать блокировок в транзакции, можно использовать фоновые задания. Рассмотрим, как это реализовать.
В событии ПриЗаписи (после успешного завершения транзакции) или в событии формы ПослеЗаписи можно запустить фоновый процесс, который выполнит HTTP-запрос в отдельном сеансе на сервере. Важно передавать в задание Ссылку на документ, а не сам Объект, так как объект в другом сеансе будет недоступен.
Посмотрим на пример реализации запуска фонового задания:
&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
// Запускаем фоновую отправку, чтобы не заставлять пользователя ждать ответа сервера
НаименованиеЗадания = "Отправка документа " + ТекущийОбъект.Ссылка;
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(ТекущийОбъект.Ссылка);
ФоновыеЗадания.Выполнить("ОбменДаннымиСервер.ОтправитьДокументФоном", ПараметрыЗадания, , НаименованиеЗадания);
КонецПроцедуры
При написании кода самой отправки через HTTPСоединение, не забывайте про настройки таймаутов. По умолчанию таймаут может быть очень длительным. Мы рекомендуем устанавливать разумные пределы, например, 10–20 секунд.
Также следует учитывать идемпотентность на стороне приемника. Это свойство системы, при котором повторный запрос с теми же данными не приводит к созданию дублей. Поскольку в случае сетевых сбоев 1С может отправить один и тот же документ дважды (например, если ответ от сервера пришел с ошибкой, хотя данные были успешно записаны), принимающая сторона должна уметь распознавать повторы по ГУИДу (UUID) документа.
Если вы работаете в современной конфигурации (УТ 11, ERP, БП 3.0), обязательно задействуйте возможности БСП. Подсистема «Очередь заданий» или механизмы «Обмена данными» уже содержат в себе все необходимые инструменты для регистрации, логирования и повтора попыток отправки сообщений. Это позволит вам сократить объем написанного кода и повысить надежность интеграции.
Подводя итог, можно сказать: всегда стремитесь разделять процесс записи данных в 1С и процесс их передачи во внешние системы. Это обеспечит высокую производительность вашей базы и гарантирует целостность информации в обеих системах.