Есть ли в 1С асинхронные HTTP-запросы и как их правильно готовить?

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

При интеграции 1С с внешними REST-сервисами разработчики часто сталкиваются с неприятной проблемой: при отправке тяжелого POST-запроса или ожидании длительного ответа от внешнего ресурса весь сеанс 1С «зависает». Пользователь видит песочные часы, интерфейс перестает реагировать, а спустя несколько минут система аварийно завершает операцию по тайм-ауту. Еще хуже, если этот процесс запускается в рамках регламентного задания на сервере: один зависший рабочий процесс rphost может заблокировать выполнение других критически важных фоновых задач.

В этой статье мы подробно разберем, как решить проблему зависания 1С при работе с HTTP-запросами. Мы проанализируем поведение устаревших COM-объектов, изучим современный нативный механизм асинхронности в платформе 1С (начиная с версий 8.3.18 и 8.3.21), а также рассмотрим проверенный временем архитектурный паттерн выполнения запросов через фоновые задания.

Почему зависают традиционные COM-объекты?

Исторически в 1С для работы с HTTP-запросами использовались системные COM-объекты Windows, такие как WinHttp.WinHttpRequest.5.1 или Msxml2.ServerXMLHTTP.6.0. При попытке вызвать метод асинхронно (например, установив соответствующий флаг при открытии соединения) разработчики сталкиваются со следующей проблемой:

Важный вывод: использование COM-объектов в современных реалиях не только делает решение жестко платформозависимым (работающим исключительно в операционной системе Windows), но и не решает задачу качественной асинхронной обработки данных в 1С.


Решение №1. Нативный асинхронный HTTP-клиент (Платформа 8.3.21 и выше)

С момента обсуждения этой проблемы фирма «1С» проделала колоссальную работу по модернизации платформы. Начиная с версии 8.3.18, в 1С появился полноценный механизм «Обещаний» (Promises) и ключевые слова Асинх и Ждать. А начиная с версии 8.3.21.1302, разработчикам стали доступны нативные асинхронные методы для работы с HTTP-соединениями.

Теперь нам не нужны внешние компоненты, библиотеки .NET или COM-объекты. Платформа предоставляет следующие асинхронные методы у объекта 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);
            // Вызываем процедуру обработки данных
            ОбработатьПолученныеДанные(ТекстОтвета);
        Иначе
            ПоказатьПредупреждение(, "Ошибка сервера. Код ответа: " + РезультатОтвета.КодСостояния);
        КонецЕсли;
        
    Исключение
        // Обрабатываем исключение (например, сетевой сбой или превышение тайм-аута)
        ЗаписатьЛогОшибки(ОписаниеОшибки());
        ПоказатьПредупреждение(, "Не удалось связаться с сервером. Подробности в логе.");
    КонецПопытки;
    
КонецПроцедуры

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

  1. Абсолютная кроссплатформенность — код без изменений работает на Windows, Linux и в веб-браузерах.
  2. Полное соответствие стандартам безопасности веб-клиентов. Современные браузеры блокируют синхронные HTTP-запросы в основном потоке, поэтому использование методов с суффиксом Асинх в Веб-клиенте является обязательным требованием.
  3. Чистый, читаемый код без использования сложных в отладке коллбэков (Callbacks) и процедур-обработчиков оповещений.

Решение №2. Асинхронность на сервере: паттерн «Фоновые задания»

Часто перед разработчиком встает задача выполнить тяжелый HTTP-запрос на сервере (например, в регламентном задании, которое запускается по расписанию каждую ночь). Важно понимать фундаментальное ограничение платформы 1С:

На сервере 1С встроенные асинхронные методы (с суффиксом Асинх) недоступны! На сервере код всегда выполняется в один поток в рамках текущей сессии. Оператор Ждать на сервере вызовет ошибку компиляции.

Как же реализовать асинхронность на сервере, чтобы длительный запрос не вешал основной поток и корректно отрабатывал в фоне? Для этого используется проверенный паттерн с запуском **Фонового задания**.

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

  1. Основной серверный код (или регламентное задание-диспетчер) инициирует фоновое задание.
  2. Фоновое задание запускается в изолированном рабочем процессе rphost и выполняет стандартный синхронный HTTP-запрос.
  3. В объекте HTTPСоединение обязательно выставляется жесткий тайм-аут, чтобы предотвратить бесконечное зависание потока при сбоях на стороне REST-сервиса.
  4. Если сервер присылает большой объем данных (например, ответ на 5 Мб), мы не читаем его в оперативную память как строку, а сразу сохраняем во временный файл на диске сервера.

Посмотрим на практическую реализацию этого паттерна в коде.

Код вызова фонового задания (например, из формы или общего модуля):


Процедура ИнициироватьПолучениеДанных() Экспорт
    
    ПараметрыЗапроса = Новый Структура;
    ПараметрыЗапроса.Вставить("АдресСервера", "api.myservice.ru");
    ПараметрыЗапроса.Вставить("Ресурс", "/v2/large_data");
    ПараметрыЗапроса.Вставить("Таймаут", 120); // 2 минуты тайм-аут
    
    МассивПараметров = Новый Массив;
    МассивПараметров.Добавить(ПараметрыЗапроса);
    
    // Запускаем фоновое задание, которое выполнит HTTP-запрос асинхронно по отношению к нам
    ФоновыеЗадания.Выполнить(
        "ИнтеграцияССервисомСервер.ВыполнитьЗапросВФоне", 
        МассивПараметров, 
        Новый УникальныйИдентификатор, 
        "Загрузка данных из REST API"
    );
    
КонецПроцедуры

Код исполняемого метода в серверном общем модуле ИнтеграцияССервисомСервер (обязательно должен быть установлен флаг «Сервер» и «Вызов сервера»):


Процедура ВыполнитьЗапросВФоне(ПараметрыЗапроса) Экспорт
    
    // Инициализируем соединение с явным указанием тайм-аута из параметров
    Соединение = Новый HTTPСоединение(
        ПараметрыЗапроса.АдресСервера, 
        , , , , 
        ПараметрыЗапроса.Таймаут
    );
    
    Запрос = Новый HTTPЗапрос(ПараметрыЗапроса.Ресурс);
    
    // Во избежание переполнения оперативной памяти при получении больших ответов (например, 5 Мб+)
    // мы настраиваем сохранение тела ответа во временный файл на сервере
    ИмяФайлаОтвета = ПолучитьИмяВременногоФайла("tmp");
    
    Попытка
        // Выполняем синхронный вызов внутри изолированного фонового процесса
        ОтветСервера = Соединение.Получить(Запрос, ИмяФайлаОтвета);
        
        Если ОтветСервера.КодСостояния = 200 Тогда
            // Файл успешно сохранен на диск. Переходим к его парсингу (например, чтением JSON)
            ОбработатьЗагруженныйФайл(ИмяФайлаОтвета);
        Иначе
            ЗаписатьОшибкуВЖурнал("Сервер вернул ошибку: " + ОтветСервера.КодСостояния);
        КонецЕсли;
        
    Исключение
        // Сюда мы попадем, если запрос отвалится по тайм-ауту или пропадет сеть
        ЗаписатьОшибкуВЖурнал("Ошибка при выполнении HTTP-запроса: " + ОписаниеОшибки());
    КонецПопытки;
    
    // Обязательно очищаем за собой временные файлы на диске сервера
    Попытка
        УдалитьФайлы(ИмяФайлаОтвета);
    Исключение
        // Игнорируем ошибку удаления, если файл не был создан
    КонецПопытки;
    
КонецПроцедуры

Решение №3. Использование внешних утилит для устаревших платформ (до версии 8.3.18)

Если вы вынуждены работать на старых версиях платформы 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С и избегать зависания системы, придерживайтесь следующих правил:

  1. Для клиентских процессов всегда используйте нативные асинхронные методы (ВызватьHTTPМетодАсинх(), ПолучитьАсинх()) с конструкцией Асинх / Ждать. Это сохранит отзывчивость интерфейса для пользователя.
  2. Для серверных процессов и регламентных заданий используйте механизм фоновых заданий (ФоновыеЗадания.Выполнить()). Выделяйте длительные интеграционные запросы в отдельные фоновые процессы.
  3. При создании объекта HTTPСоединение всегда указывайте тайм-аут. Значение по умолчанию может приводить к бесконечным зависаниям при сетевых сбоях. Оптимальный тайм-аут для большинства интеграций — от 30 до 120 секунд.
  4. Если объем принимаемых данных велик (более 2-3 Мегабайт), никогда не считывайте тело ответа в строку в оперативную память. Используйте методы записи HTTP-ответа непосредственно в файл на диске. Это убережет рабочий процесс сервера от утечек памяти и аварийного завершения.
← На главную