При организации обмена данными между базами 1С через SOAP веб-сервисы разработчики нередко сталкиваются с ситуацией: небольшие пакеты данных обрабатываются мгновенно, но при передаче объемного пакета клиентская база зависает. В журнале регистрации или логах сервера видно, что экспортная функция веб-сервиса успешно отработала и вернула результат, однако клиент так и не получает управление обратно, продолжая ожидать ответа вплоть до ошибки «Превышен таймаут операции».
Разберем природу этого поведения, выясним причины сбоя на сетевом и прикладном уровнях, а также рассмотрим практические способы решения проблемы.
Проанализируем механику взаимодействия клиента и веб-сервиса. Когда мы вызываем метод сервиса через объект WSПрокси, клиент отправляет HTTP-запрос и переходит в режим блокирующего ожидания ответа по установленному TCP-соединению:
Прокси = ПолучитьПрокси();
ДанныеОбмена = СформироватьПакетОбмена(Узел);
// Вызов зависает на длительное время:
Прокси.ЗаписатьДанные(КодЭтогоУзла, ДанныеОбмена);
Если серверная процедура выполняется 20–30 минут, соединение оказывается в состоянии простоя (idle): по сокету не передаются пакеты с полезными данными. Это приводит к следующим системным проблемам:
TCP RST или FIN. Сервер завершает транзакцию и отправляет ответ в уже разорванный канал, а клиент продолжает ждать ответа до истечения локального таймаута.Idle Time-out) по умолчанию составляет 20 минут. Если ответ от модуля расширения 1С задерживается, веб-сервер прерывает соединение с клиентом, хотя рабочий процесс rphost может продолжать выполнение кода.Держать открытым синхронное HTTP/SOAP-соединение более 1–2 минут — грубая архитектурная ошибка. Правильный подход заключается в разделении вызова на прием данных и их отложенную обработку в фоновом режиме.
Разберем по шагам реализацию асинхронного взаимодействия:
УникальныйИдентификатор). Соединение закрывается за пару секунд.Посмотрим на пример серверной реализации метода веб-сервиса:
Функция ЗаписатьДанныеАсинхронно(КодУзла, Данные) Экспорт
// Сохраняем пакет в базу (например, во временную таблицу или регистр)
ИдентификаторЗадания = СохранитьПакетВоВременноеХранилище(КодУзла, Данные);
// Параметры для фоновой процедуры
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(ИдентификаторЗадания);
ПараметрыЗадания.Добавить(КодУзла);
// Запускаем тяжелую обработку в фоне
ФоновыеЗадания.Выполнить("ИнтеграцияСервер.ВыполнитьФоновуюЗагрузку", ПараметрыЗадания, Строка(ИдентификаторЗадания), "Загрузка данных обмена");
// Сразу возвращаем клиенту GUID операции
Возврат Строка(ИдентификаторЗадания);
КонецФункции
На стороне клиента организуем периодическую проверку завершения операции:
Прокси = ПолучитьПрокси();
ИдентификаторЗадания = Прокси.ЗаписатьДанныеАсинхронно(КодЭтогоУзла, ДанныеОбмена);
// Ожидаем завершения без зависания основного соединения
Статус = "Выполняется";
Пока Статус = "Выполняется" Цикл
// Пауза между опросами статуса (например, 10 секунд)
ОжидатьЗавершения(10);
Статус = Прокси.ПолучитьСтатусЗадания(ИдентификаторЗадания);
Если Статус = "Ошибка" Тогда
ВызватьИсключение "Ошибка фоновой обработки на сервере!";
КонецЕсли;
КонецЦикла;
Если переделка логики на фоновые задания требует слишком больших трудозатрат, рассмотрим альтернативный вариант — пакетирование передаваемых данных. Вместо отправки одного монолитного файла, обработка которого занимает 30 минут, разобьем данные на стороне отправителя на небольшие порции (например, по 500–1000 документов).
Каждая порция отправляется отдельным запросом в рамках собственной короткой транзакции. Если при передаче одной из пачек возникнет сетевой сбой, нам не придется откатывать всю получасовую работу — достаточно будет повторить отправку упавшего пакета.
Если требуется временно обеспечить прохождение существующих тяжелых вызовов без изменения программного кода, необходимо скорректировать сетевые параметры серверов.
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters скорректируем параметр KeepAliveTime, уменьшив его со стандартных 2 часов до 60 000–120 000 мс (1–2 минуты).poolTimeout (время жизни соединения в пуле) и установим параметр reuseSessions="autouse" или reuseSessions="dontuse", чтобы избежать блокировок зависших сеансов при повторных вызовах.WSПрокси в платформе 1С явно передается достаточный таймаут вызова (в секундах), перекрывающий максимальное время работы серверного кода:
// Передаем таймаут в секундах (например, 3600 сек = 1 час) последним параметром
Прокси = Новый WSПрокси(Определение, URIПространстваИмен, ИмяСервиса, ИмяТочкиПодключения, , 3600);
Подведем итог: зависание синхронного вызова при длительной обработке — это закономерный результат разрыва сетевых сокетов по таймауту неактивности. Наиболее надежным и стабильным решением всегда остается отказ от удержания открытого HTTP-соединения в пользу асинхронных фоновых заданий с получением статуса готовности.