При реализации интеграционных механизмов между 1С и внешними системами (например, Битрикс24, CRM-системами или веб-сервисами) разработчики часто сталкиваются с крайне неприятным поведением: один и тот же запрос на создание сущности (метод POST) срабатывает дважды. В результате в принимающей базе создаются дублирующиеся контакты, компании или заказы, причем временной зазор между запросами в логах веб-сервера может составлять всего несколько миллисекунд.
В этой статье мы подробно разберем, почему функция HTTPСоединение.ОтправитьДляОбработки() может дублировать запросы, проанализируем неочевидные особенности работы инструментов разработчика и рассмотрим сугубо технические аспекты работы сетевого стека платформы 1С.
Начнем с разбора ситуации, с которой столкнулся автор темы. Во время отладки обмена программисты часто используют внешние обработки для управления регламентными заданиями. Одной из самых популярных является Консоль заданий из пакета «Инструменты разработчика» (ИР).
Рассмотрим, что происходит, когда вы выбираете задание и нажимаете кнопку «Выполнить – На сервере»:
В результате на сервере 1С параллельно стартуют два потока. Поскольку в коде 1С перед отправкой запроса обычно проверяется признак «выгружено» в информационной базе, оба потока одновременно считывают из СУБД состояние «Не выгружено» (так как транзакции еще не зафиксированы). Оба потока формируют одинаковый запрос и отправляют его в Битрикс24. На стороне Битрикса создаются два элемента с разными внутренними идентификаторами, а 1С получает два успешных ответа и дважды перезаписывает признак выгрузки.
Решение проблемы:
Для предотвращения подобных ситуаций при отладке и в промышленной эксплуатации необходимо использовать механизм транзакционных блокировок или контролировать уникальность выполняемого процесса на уровне кода базы данных. Разберем пример реализации безопасного запуска через механизм БлокировкаДанных:
Процедура ВыполнитьОбменСБитрикс() Экспорт
// Устанавливаем исключительную блокировку на константу или специальный регистр управления
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СостояниеОбменаДанными");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
НачатьТранзакцию();
Попытка
Блокировка.Заблокировать();
// Выполняем чтение данных и отправку HTTP-запросов
// ...
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ЗаписатьЛогОшибок(ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
При таком подходе второй параллельный поток, запущенный случайно из консоли регламентных заданий, не сможет начать чтение данных до тех пор, пока первый поток не завершит транзакцию и не обновит статусы выгрузки.
Еще одна частая скрытая причина двойной отправки HTTP-запросов кроется во внутреннем поведении платформы 1С при работе с объектом HTTPСоединение.
Если параметры авторизации (логин и пароль) передаются непосредственно в конструктор соединения, платформа ведет себя следующим образом:
Authorization), предполагая оптимистичный сценарий.401 Unauthorized, присылая в заголовке WWW-Authenticate список поддерживаемых методов авторизации (Basic, Digest, NTLM).В чем опасность? Если принимающий веб-сервер (или скрипт-обработчик) настроен некорректно и начинает выполнять бизнес-логику создания объектов (например, в Битрикс24) до полной валидации заголовков авторизации или если он обрабатывает тело пустого первого запроса, это приводит к генерации дублей записей или к ошибкам в логах.
Решение проблемы:
Рекомендуется полностью отказаться от передачи учетных данных в конструктор HTTPСоединение. Заголовки авторизации следует формировать вручную и явно добавлять их в объект HTTPЗапрос. Посмотрим на практический пример:
// Настройка соединения без указания логина и пароля в конструкторе
Соединение = Новый HTTPСоединение("ваш-портал.bitrix24.ru", 443, , , , Истина);
Запрос = Новый HTTPЗапрос("/rest/123/webhook/crm.contact.add.json");
Запрос.Заголовки.Вставить("Content-Type", "application/json");
// Формируем заголовок Basic-авторизации вручную
ДанныеАвторизации = ИмяПользователя + ":" + ПарольПользователя;
СтрокаBase64 = ПолучитьBase64СтрокуИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзСтроки(ДанныеАвторизации));
Запрос.Заголовки.Вставить("Authorization", "Basic " + СтрокаBase64);
// Отправляем подготовленный запрос
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
При таком подходе авторизационный заголовок уходит сразу же с самым первым сетевым пакетом, исключая отправку проверочных запросов и снижая нагрузку на сеть. Для интерактивной проверки и отладки HTTP-запросов с кастомными заголовками авторизации можно использовать обработку Post1С для тестирования REST API.
Для выполнения HTTP-запросов платформа 1С использует системные библиотеки операционной системы: WinHTTP на ОС Windows и libcurl на ОС Linux.
Если в процессе выполнения тяжелого POST-запроса на создание сущности происходят кратковременные сетевые сбои, потеря пакетов или удаленный сервер слишком долго обрабатывает транзакцию и превышается таймаут ожидания (который задается в конструкторе HTTPСоединение), на уровне сетевой библиотеки может сработать логика повторной отправки пакетов (ретрансляция TCP).
Если первый запрос успешно дошел до Битрикс24 и система создала контакт, но сетевой стек 1С не получил ответную квитанцию вовремя, платформа посчитает запрос аварийным. При повторной автоматической отправке пакетов (или при ручном перезапуске транзакции в коде 1С после получения исключения по таймауту) произойдет повторное создание сущности.
Решение проблемы:
Метод POST по спецификации HTTP является **неидемпотентным** — это значит, что каждый повторный вызов приводит к изменениям на сервере. Чтобы защитить систему от дублирования данных при сетевых сбоях, необходимо реализовывать логику идемпотентности на уровне API:
crm.contact.add всегда делайте быстрый поисковый запрос (метод crm.contact.list с фильтром по вашему UUID), чтобы убедиться, что данный объект не был создан секунду назад во время сбойного запроса.Часто между сервером 1С и конечной облачной системой (Битрикс24) располагаются корпоративные прокси-серверы или балансировщики нагрузки (такие как Nginx, IIS, Apache или HAProxy).
Проанализируем классическую ситуацию: веб-сервер Nginx настроен как обратный прокси. При отправке запроса из 1С, Nginx перенаправляет его на бэкенд-сервер. Если бэкенд отвечает ошибкой 502 Bad Gateway или 504 Gateway Timeout, Nginx по умолчанию (в зависимости от конфигурации директивы proxy_next_upstream) может решить, что текущий сервер бэкенда недоступен, и автоматически перенаправить **тот же самый POST-запрос** на другой рабочий процесс или сервер в кластере.
Решение проблемы:
Необходимо скорректировать настройки промежуточного прокси-сервера. Для неидемпотентных методов передачи данных автоматические повторы должны быть строго запрещены. В конфигурационном файле Nginx за это отвечает следующая настройка:
# Отключаем повторные попытки отправки запросов для методов, отличных от GET
proxy_next_upstream error timeout invalid_header;
# Исключаем использование параметра non_idempotent для POST/PUT запросов
Если при настройке обмена в коде 1С адрес запроса указан по незащищенному протоколу http://..., а внешняя система принудительно переводит всех клиентов на защищенный протокол https://..., происходит HTTP-редирект с кодами состояния 301 Moved Permanently, 302 Found, или современными 307 Temporary Redirect и 308 Permanent Redirect.
Согласно спецификации протокола HTTP, при получении редиректов 307 и 308 клиент (в данном случае платформа 1С) обязан повторить исходный POST-запрос на новый адрес с сохранением тела запроса (Payload). Если на первом этапе веб-сервер частично обработал данные до выполнения редиректа, повторный запрос на HTTPS-адрес приведет к неконтролируемому дублированию. Для быстрой проверки корректности адреса и доступности точки интеграции подойдёт утилита для проверки доступности вебхука Битрикс24 из 1С.
Решение проблемы:
Всегда прописывайте в коде 1С точные, конечные адреса запросов. Исключайте любые промежуточные звенья:
https:// в конфигурационных файлах и константах 1С./) на конце адресной строки запроса (многие веб-серверы делают автоматический редирект при его отсутствии).Мы подробно проанализировали причины двойного срабатывания запросов при интеграции 1С. В большинстве случаев виной тому является человеческий фактор при отладке регламентных заданий (параллельный запуск фоновых задач на сервере), особенности двухэтапной авторизации платформы 1С или поведение сетевого оборудования при обработке ошибок. Следуя приведенным инструкциям и примерам кода, вы сможете построить надежную и отказоустойчивую систему интеграции без дублирования транзакций.