При интеграции внешних систем с информационной базой 1С через HTTP-сервисы разработчики нередко сталкиваются с ситуацией, когда первые вызовы метода отрабатывают штатно, а затем сервис намертво зависает. При этом подключение остается висеть в активных сеансах, обработчик метода перестает вызываться, а веб-сервер IIS выдает системную ошибку о том, что рабочий процесс не смог остановить канал прослушивателя для протокола HTTP в отведенный интервал времени.
Ситуация осложняется тем, что попытка перехватить ошибку стандартной конструкцией Попытка ... Исключение не срабатывает — сервис зависает непосредственно в момент вызова метода Записать(). Разберем по шагам, почему возникает эта проблема в файловом режиме и какими способами ее можно устранить.
Проанализируем техническую сторону взаимодействия веб-сервера Internet Information Services (IIS) и файловой СУБД платформы 1С:
wsisapi.dll выполняется внутри рабочего процесса IIS (w3wp.exe). Файловая база управляет доступом к данным посредством системных блокировок диапазонов байт в файле 1Cv8.1CD. Если входящие HTTP-запросы приходят плотным потоком или в цикле выполняется множество одиночных записей, потоки процесса начинают конкурировать за файловый дескриптор. Происходит взаимоблокировка (deadlock) на уровне операционной системы, которую код 1С перехватить не может.Записать() вызывается без явного открытия транзакции, платформа на каждую итерацию открывает неявную транзакцию, фиксирует изменения и выполняет сброс буферов на диск. При нескольких сотнях таких вызовов файловая подсистема перегружается операциями ввода-вывода.reuseSessions="autouse" в конфигурационном файле публикации default.vrd заставляет веб-сервер удерживать сеансы в оперативной памяти. Если один из потоков завис в ожидании снятия блокировки с файла базы данных, сеанс из пула не освобождается. По достижении предела poolSize все последующие запросы становятся в бесконечную очередь.w3wp.exe, так как системные потоки удерживают открытый файл базы данных. В результате порт блокируется, и сервер прекращает обработку входящего трафика.Посмотрим на исходный проблемный фрагмент кода, где запись менеджера регистра сведений выполняется в цикле без явного управления транзакциями:
Функция ШаблонURL1Example(Запрос)
СтрокаОтвет = "Успех!";
Для Сч = 1 по 300 Цикл
Запись = РегистрыСведений.КурсыВалют.СоздатьМенеджерЗаписи();
Запись.Период = Дата(2017, 1, 1);
Запись.Валюта = Константы.ВалютаРегламентированногоУчета.Получить();
Запись.Курс = 1;
Запись.Кратность = 1;
Попытка
Запись.Записать();
Исключение
СтрокаОтвет = "Запись не удалась. Попытка № " + Сч + ". " + ОписаниеОшибки();
Прервать;
КонецПопытки;
КонецЦикла;
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзСтроки(СтрокаОтвет);
Возврат Ответ;
КонецФункции
В этом коде 300 раз подряд захватывается и освобождается файл 1Cv8.1CD. Чтобы устранить конкуренцию и снизить нагрузку на дисковую подсистему, объединим весь массив операций в одну явную транзакцию с помощью методов НачатьТранзакцию() и ЗафиксироватьТранзакцию(), а также поместим блок перехвата ошибок снаружи цикла:
Функция ШаблонURL1Example(Запрос)
СтрокаОтвет = "Успех!";
СчСервиса = 0;
НачатьТранзакцию();
Попытка
Для Сч = 1 По 300 Цикл
СчСервиса = Сч;
Запись = РегистрыСведений.КурсыВалют.СоздатьМенеджерЗаписи();
Запись.Период = Дата(2017, 1, 1);
Запись.Валюта = Константы.ВалютаРегламентированногоУчета.Получить();
Запись.Курс = 1;
Запись.Кратность = 1;
Запись.Записать();
КонецЦикла;
ЗафиксироватьТранзакцию();
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзСтроки(СтрокаОтвет);
Исключение
ОтменитьТранзакцию();
СтрокаОтвет = "Запись не удалась на итерации № " + СчСервиса + ". " + ОписаниеОшибки();
Ответ = Новый HTTPСервисОтвет(500);
Ответ.УстановитьТелоИзСтроки(СтрокаОтвет);
КонецПопытки;
Возврат Ответ;
КонецФункции
Такой подход сокращает количество операций блокировки на уровне диска с сотен до единичного вызова, что предотвращает появление дедлоков операционной системы.
Если специфика сервиса требует обработки частых одиночных запросов от внешних систем, необходимо скорректировать параметры самого веб-сервера:
1C_Pool) и уберем ее из общего пула DefaultAppPool.Max Worker Processes). Он должен быть строго равен 1. Использование веб-сада (Web Garden) при файловой базе недопустимо, так как два независимых процесса w3wp.exe не могут одновременно разделять монопольный доступ к файлу базы.No Managed Code), поскольку библиотека wsisapi.dll является нативным модулем C++.Load User Profile) в значение False для снижения блокировок системного реестра.
<service name="CRMIntegration"
rootUrl="CRMIntegration"
enable="true"
reuseSessions="dontuse"/>
reuseSessions="dontuse" заставляет сервис завершать сеанс после отдачи ответа клиенту. Это предотвращает утечку зависших подключений в пуле, хотя и создает небольшие накладные расходы на инициализацию контекста при каждом запросе.Практика показывает, что многопоточная модель взаимодействия модуля wsisapi.dll с файловой СУБД 1С под управлением IIS часто оказывается нестабильной при высокой частоте запросов. Перенос публикации на веб-сервер Apache HTTP Server (версии 2.4) с модулем mod_1c.so решает проблему зависания без необходимости переписывать алгоритмы интеграции. Модуль Apache иначе управляет потоками и блокировками дескрипторов файлов базы, что обеспечивает стабильную работу даже при серийной записи данных в файловую базу.
Для долговременной стабильности интеграций рассмотрим следующие рекомендации:
*.1CD добавлены в список исключений антивирусного сканера и службы индексации Windows Search, которые часто перехватывают дескрипторы файлов в момент интенсивной записи.200 OK, а разбор и запись в базу должны выполняться регламентным заданием в один поток.rphost, а блокировки переходят на уровень строк СУБД, что полностью исключает зависание веб-сервера.