Реализация REST-клиента и пула HTTP-соединений в 1С:Предприятие
При интенсивном выполнении исходящих HTTP-запросов из 1С возникают проблемы:
- Исчерпание доступных TCP-портов операционной системы (
TIME_WAIT TCP Port Exhaustion). - Высокие накладные расходы времени (>100-300 мс) на установку TLS-рукопожатия при каждом вызове метода.
- Падение производительности сервера приложений при параллельных запросах из фоновых потоков.
- Ошибки
Не удалось открыть сокет / Connection reset by peerпри частых обращениях к внешнему API.
1. Проблема постоянного создания объекта HTTPСоединение
Создание нового экземпляра HTTPСоединение на каждый запрос заставляет ОС выполнять 3-way TCP handshake и тяжелое SSL/TLS рукопожатие, оставляя закрытый сокет в состоянии TIME_WAIT на 60-120 секунд. Пул соединений (Connection Pooling) сохраняет открытые TCP-сокеты с заголовком Connection: keep-alive для повторных запросов.
2. Программный модуль синглтона пула HTTP-соединений
// Общий модуль: ПулHTTPСоединенийПовтИсп (Повторное использование: На время сеанса / На время вызова)
Функция ПолучитьHTTPСоединение(Хост, Порт = 443, ИспользоватьSSL = Истина, Таймаут = 30) Экспорт
ПараметрыЗащиты = Неопределено;
Если ИспользоватьSSL Тогда
ПараметрыTLS = Новый ПараметрыЗащищенногоСоединенияOpenSSL();
ПараметрыЗащиты = Новый ЗащищенноеСоединениеOpenSSL(ПараметрыTLS);
КонецЕсли;
// Платформа 1С кэширует физический сокет внутри объекта HTTPСоединение
Соединение = Новый HTTPСоединение(
Хост,
Порт,
,
,
,
Таймаут,
ПараметрыЗащиты
);
Возврат Соединение;
КонецФункции3. Обертка выполнения REST-запросов с поддержкой Keep-Alive
&НаСервере
Функция ВыполнитьRESTЗапрос(МетодHTTP, Хост, Путь, ПараметрыJSON = "") Экспорт
// Извлечение соединения из кэша сеанса
HTTPСоединение = ПулHTTPСоединенийПовтИсп.ПолучитьHTTPСоединение(Хост, 443, Истина, 15);
HTTPЗапрос = Новый HTTPЗапрос(Путь);
HTTPЗапрос.Заголовки.Вставить("Host", Хост);
HTTPЗапрос.Заголовки.Вставить("Connection", "keep-alive");
HTTPЗапрос.Заголовки.Вставить("Accept", "application/json");
Если НЕ ПустаяСтрока(ПараметрыJSON) Тогда
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
HTTPЗапрос.УстановитьТелоИзСтроки(ПараметрыJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
КонецЕсли;
Попытка
Ответ = HTTPСоединение.ВызватьHTTPМетод(МетодHTTP, HTTPЗапрос);
Возврат Ответ;
Исключение
ЗаписьЖурналаРегистрации("REST_CLIENT", УровеньЖурналаРегистрации.Ошибка,,,
СтрШаблон("Ошибка вызова %1 https://%2%3: %4", МетодHTTP, Хост, Путь, ОписаниеОшибки()));
ВызватьИсключение;
КонецПопытки;
КонецФункции Частые вопросы (FAQ)
Поддерживает ли платформа 1С HTTP/2?
Встроенный объект HTTPСоединение работает по протоколу HTTP/1.1 с поддержкой конвейерной обработки (Pipelining) и постоянных соединений Keep-Alive.
Где лучше кэшировать объекты HTTPСоединение?
Используйте общие модули с признаком 'Повторное использование возвращаемых значений' (На время вызова или На время сеанса) для разделения соединений по хостам.
Как внешнему серверу сообщить о закрытии соединения?
Передайте заголовок HTTPЗапрос.Заголовки.Вставить('Connection', 'close') — после завершения запроса сокет будет немедленно закрыт сервером.
Что делать при ошибке 'Connection reset by peer' при использовании пула?
Внешний балансировщик закрыл Keep-Alive сокет по таймауту неактивности. Реализуйте перехват исключения с повторным созданием объекта соединения и повтором вызова.