При интеграции 1С с внешними REST-сервисами разработчики часто сталкиваются с неприятной проблемой: при отправке тяжелого POST-запроса или ожидании длительного ответа от внешнего ресурса весь сеанс 1С «зависает». Пользователь видит песочные часы, интерфейс перестает реагировать, а спустя несколько минут система аварийно завершает операцию по тайм-ауту. Еще хуже, если этот процесс запускается в рамках регламентного задания на сервере: один зависший рабочий процесс rphost может заблокировать выполнение других критически важных фоновых задач.
В этой статье мы подробно разберем, как решить проблему зависания 1С при работе с HTTP-запросами. Мы проанализируем поведение устаревших COM-объектов, изучим современный нативный механизм асинхронности в платформе 1С (начиная с версий 8.3.18 и 8.3.21), а также рассмотрим проверенный временем архитектурный паттерн выполнения запросов через фоновые задания.
Исторически в 1С для работы с HTTP-запросами использовались системные COM-объекты Windows, такие как WinHttp.WinHttpRequest.5.1 или Msxml2.ServerXMLHTTP.6.0. При попытке вызвать метод асинхронно (например, установив соответствующий флаг при открытии соединения) разработчики сталкиваются со следующей проблемой:
WinHttpRequest метод WaitForResponse возвращает управление по истечении тайм-аута, однако прочитать ResponseText или ResponseBody невозможно, так как данные еще не получены полностью.ServerXMLHTTP свойство ReadyState переходит в состояние 3 (идет загрузка данных), но платформа 1С не имеет возможности штатно и без блокировки основного потока дождаться завершения загрузки или обработать событие окончания приема пакета данных.Важный вывод: использование COM-объектов в современных реалиях не только делает решение жестко платформозависимым (работающим исключительно в операционной системе Windows), но и не решает задачу качественной асинхронной обработки данных в 1С.
С момента обсуждения этой проблемы фирма «1С» проделала колоссальную работу по модернизации платформы. Начиная с версии 8.3.18, в 1С появился полноценный механизм «Обещаний» (Promises) и ключевые слова Асинх и Ждать. А начиная с версии 8.3.21.1302, разработчикам стали доступны нативные асинхронные методы для работы с HTTP-соединениями.
Теперь нам не нужны внешние компоненты, библиотеки .NET или COM-объекты. Платформа предоставляет следующие асинхронные методы у объекта HTTPСоединение:
ПолучитьАсинх()ВызватьHTTPМетодАсинх()ЗаписатьАсинх()ОтправитьДляОбработкиАсинх()Рассмотрим детально, как выглядит современный асинхронный запрос в коде 1С. Этот метод идеально подходит для выполнения на клиенте (в Тонком, Толстом или Веб-клиенте), поскольку он гарантирует, что пользовательский интерфейс останется полностью отзывчивым во время загрузки данных.
&НаКлиенте
Асинх Процедура ВыполнитьЗапросКСервисуАсинхронно()
// 1. Инициализируем защищенное соединение и HTTP-соединение
Защита = Новый ЗащищенноеСоединениеOpenSSL();
Соединение = Новый HTTPСоединение("api.myservice.ru", , , , , 60, Защита);
// 2. Формируем запрос
Запрос = Новый HTTPЗапрос("/v2/get_heavy_data");
Запрос.УстановитьТелоИзСтроки("{""request"":""get_payload""}", КодировкаТекста.UTF8);
ПоказатьПредупреждение(, "Запрос отправлен. Вы можете продолжать работу с интерфейсом!");
Попытка
// 3. Вызываем асинхронный метод и ожидаем результат через оператор Ждать
// В этот момент выполнение процедуры приостанавливается, но интерфейс 1С НЕ ЗАВИСАЕТ!
РезультатОтвета = Ждать Соединение.ВызватьHTTPМетодАсинх("POST", Запрос);
// 4. Обрабатываем полученный результат после завершения ожидания
Если РезультатОтвета.КодСостояния = 200 Тогда
ТекстОтвета = РезультатОтвета.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
// Вызываем процедуру обработки данных
ОбработатьПолученныеДанные(ТекстОтвета);
Иначе
ПоказатьПредупреждение(, "Ошибка сервера. Код ответа: " + РезультатОтвета.КодСостояния);
КонецЕсли;
Исключение
// Обрабатываем исключение (например, сетевой сбой или превышение тайм-аута)
ЗаписатьЛогОшибки(ОписаниеОшибки());
ПоказатьПредупреждение(, "Не удалось связаться с сервером. Подробности в логе.");
КонецПопытки;
КонецПроцедуры
Преимущества данного подхода:
Асинх в Веб-клиенте является обязательным требованием.Часто перед разработчиком встает задача выполнить тяжелый HTTP-запрос на сервере (например, в регламентном задании, которое запускается по расписанию каждую ночь). Важно понимать фундаментальное ограничение платформы 1С:
На сервере 1С встроенные асинхронные методы (с суффиксом Асинх) недоступны! На сервере код всегда выполняется в один поток в рамках текущей сессии. Оператор Ждать на сервере вызовет ошибку компиляции.
Как же реализовать асинхронность на сервере, чтобы длительный запрос не вешал основной поток и корректно отрабатывал в фоне? Для этого используется проверенный паттерн с запуском **Фонового задания**.
Разберем этот алгоритм по шагам:
rphost и выполняет стандартный синхронный HTTP-запрос.HTTPСоединение обязательно выставляется жесткий тайм-аут, чтобы предотвратить бесконечное зависание потока при сбоях на стороне REST-сервиса.Посмотрим на практическую реализацию этого паттерна в коде.
Код вызова фонового задания (например, из формы или общего модуля):
Процедура ИнициироватьПолучениеДанных() Экспорт
ПараметрыЗапроса = Новый Структура;
ПараметрыЗапроса.Вставить("АдресСервера", "api.myservice.ru");
ПараметрыЗапроса.Вставить("Ресурс", "/v2/large_data");
ПараметрыЗапроса.Вставить("Таймаут", 120); // 2 минуты тайм-аут
МассивПараметров = Новый Массив;
МассивПараметров.Добавить(ПараметрыЗапроса);
// Запускаем фоновое задание, которое выполнит HTTP-запрос асинхронно по отношению к нам
ФоновыеЗадания.Выполнить(
"ИнтеграцияССервисомСервер.ВыполнитьЗапросВФоне",
МассивПараметров,
Новый УникальныйИдентификатор,
"Загрузка данных из REST API"
);
КонецПроцедуры
Код исполняемого метода в серверном общем модуле ИнтеграцияССервисомСервер (обязательно должен быть установлен флаг «Сервер» и «Вызов сервера»):
Процедура ВыполнитьЗапросВФоне(ПараметрыЗапроса) Экспорт
// Инициализируем соединение с явным указанием тайм-аута из параметров
Соединение = Новый HTTPСоединение(
ПараметрыЗапроса.АдресСервера,
, , , ,
ПараметрыЗапроса.Таймаут
);
Запрос = Новый HTTPЗапрос(ПараметрыЗапроса.Ресурс);
// Во избежание переполнения оперативной памяти при получении больших ответов (например, 5 Мб+)
// мы настраиваем сохранение тела ответа во временный файл на сервере
ИмяФайлаОтвета = ПолучитьИмяВременногоФайла("tmp");
Попытка
// Выполняем синхронный вызов внутри изолированного фонового процесса
ОтветСервера = Соединение.Получить(Запрос, ИмяФайлаОтвета);
Если ОтветСервера.КодСостояния = 200 Тогда
// Файл успешно сохранен на диск. Переходим к его парсингу (например, чтением JSON)
ОбработатьЗагруженныйФайл(ИмяФайлаОтвета);
Иначе
ЗаписатьОшибкуВЖурнал("Сервер вернул ошибку: " + ОтветСервера.КодСостояния);
КонецЕсли;
Исключение
// Сюда мы попадем, если запрос отвалится по тайм-ауту или пропадет сеть
ЗаписатьОшибкуВЖурнал("Ошибка при выполнении HTTP-запроса: " + ОписаниеОшибки());
КонецПопытки;
// Обязательно очищаем за собой временные файлы на диске сервера
Попытка
УдалитьФайлы(ИмяФайлаОтвета);
Исключение
// Игнорируем ошибку удаления, если файл не был создан
КонецПопытки;
КонецПроцедуры
Если вы вынуждены работать на старых версиях платформы 1С, где нет нативной поддержки асинхронности, а стандартные методы зависают из-за особенностей сетевого оборудования или специфики удаленного сервера, можно применить обходное решение — вызов консольной утилиты cURL через объект операционной системы Windows WScript.Shell.
Этот метод является платформозависимым и считается «костылем», однако он эффективно решает задачу асинхронной отправки запроса и возврата контроля в 1С без зависания сеанса:
Процедура ОтправитьЗапросЧерезCurlАсинхронно(АдресРесурса, ПутьКФайлуОтвета)
Попытка
Shell = Новый COMОбъект("WScript.Shell");
// Формируем консольную команду для curl
// Флаг -s отключает индикатор прогресса, -o указывает файл для сохранения ответа
Команда = "curl -s -X POST """ + АдресРесурса + """ -o """ + ПутьКФайлуОтвета + """ --max-time 180";
// Третий параметр "Ложь" указывает WScript.Shell запустить команду асинхронно,
// то есть не ожидать завершения ее выполнения и сразу вернуть управление в 1С!
Shell.Run(Команда, 0, Ложь);
// 1С продолжает работу мгновенно, curl выполняется в фоне ОС
Исключение
ВызватьИсключение "Не удалось запустить внешний HTTP-клиент: " + ОписаниеОшибки();
КонецПопытки;
КонецПроцедуры
Важное предупреждение: Используйте данный метод только в крайних случаях, когда переход на новые версии платформы невозможен, а штатные фоновые задания по какой-то причине не подходят для вашей архитектуры. Этот код привязывает вашу систему к серверам под управлением ОС Windows и требует наличия установленной утилиты curl в системе.
Для того чтобы эффективно решать задачи интеграции в 1С и избегать зависания системы, придерживайтесь следующих правил:
ВызватьHTTPМетодАсинх(), ПолучитьАсинх()) с конструкцией Асинх / Ждать. Это сохранит отзывчивость интерфейса для пользователя.ФоновыеЗадания.Выполнить()). Выделяйте длительные интеграционные запросы в отдельные фоновые процессы.HTTPСоединение всегда указывайте тайм-аут. Значение по умолчанию может приводить к бесконечным зависаниям при сетевых сбоях. Оптимальный тайм-аут для большинства интеграций — от 30 до 120 секунд.