Почему процедура на сервере 1С начинает выполняться заново сама по себе?

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

В практике разработки на платформе 1С:Предприятие 8.3 иногда встречается пугающая ситуация: запущенная серверная процедура, выполняющая длительную обработку данных (например, выгрузку тяжелых пакетов через КД 2.0), внезапно прерывается и начинает свое выполнение с самой первой строки. При этом отладчик может повторно останавливаться на точке останова в начале процедуры, хотя цикл внутри еще не был завершен. Рассмотрим подробнее, почему это происходит и как устранить этот «мистический» эффект.

Анализ ситуации: механизм «фантомного» перезапуска

Когда мы запускаем длительный процесс на сервере из формы (через обычную команду), клиентское приложение ожидает ответа от сервера. Если информационная база опубликована через веб-сервер (IIS или Apache), в цепочку взаимодействия включается протокол HTTP. Рассмотрим причины, по которым процедура может стартовать заново:

  1. Повторные HTTP-запросы (Retry) веб-сервера или прокси. Это самая частая причина. Если выполнение серверного метода ВыполнитьВыгрузку занимает время, превышающее таймаут веб-сервера (по умолчанию часто 60 или 120 секунд), веб-сервер или промежуточный прокси-сервер (например, Nginx) может посчитать, что запрос «потерялся». В этом случае он автоматически инициирует повторный POST-запрос. Платформа 1С воспринимает это как новый вызов команды пользователем, и процедура запускается во втором потоке с самого начала.
  2. Перезапуск рабочего процесса rphost. Обработка УниверсальныйОбменДаннымиXML потребляет значительный объем оперативной памяти. Если потребление памяти превышает допустимые пределы, настроенные в кластере 1С, процесс rphost может быть принудительно завершен. В некоторых случаях клиентское приложение пытается восстановить соединение и повторить последнюю операцию.
  3. Особенности отладки. При потере связи между конфигуратором и рабочим процессом (из-за тех же таймаутов) отладчик может вести себя непредсказуемо, визуально «перепрыгивая» на начало исполняемого блока при восстановлении сессии.

Проанализируем код, который часто приводит к таким последствиям. В нем создается объект обработки внутри цикла, что само по себе ресурсозатратно:


&НаСервере
Процедура ВыгрузитьДанные()
    ВсеПакетыВыгружены = Ложь;
    Пока НЕ ВсеПакетыВыгружены Цикл
        // Создание объекта в цикле создает лишнюю нагрузку на память
        Обработка = ВнешниеОбработки.Создать(ПутьКФайлу, Ложь); 
        Обработка.РежимОбмена = "Выгрузка";
        // ... настройка параметров ...
        Обработка.ВыполнитьВыгрузку(); // Здесь происходит длительная пауза
        
        // Условие выхода
        Если ТекущийКод >= МаксимальныйНомер Тогда
            ВсеПакетыВыгружены = Истина;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Решение 1: Использование фоновых заданий

Чтобы избежать зависимости от таймаутов HTTP-соединения и «зависания» интерфейса пользователя, длительные операции необходимо выносить в фоновые задания. Это кардинально решает проблему повторных вызовов, так как клиент не ждет завершения HTTP-запроса, а просто запускает процесс на сервере и периодически проверяет его состояние.

Разберем, как правильно перевести выгрузку в фон. Вместо прямого вызова процедуры из команды формы, используем менеджер фоновых заданий:


&НаКлиенте
Процедура ВыполнитьВыгрузку(Команда)
    ПараметрыВыгрузки = Новый Структура;
    ПараметрыВыгрузки.Вставить("ДатаВыгрузки", Объект.ДатаВыгрузки);
    ПараметрыВыгрузки.Вставить("ПутьКФайлуПравил", Объект.ПутьКФайлуПравил);
    
    // Запускаем фоновое задание
    ФоновоеЗадание = ЗапуститьВыгрузкуНаСервере(ПараметрыВыгрузки);
    ИдентификаторЗадания = ФоновоеЗадание.УникальныйИдентификатор;
    
    // Подключаем обработчик ожидания для проверки статуса
    ПодключитьОбработчикОжидания("ПроверитьСтатусВыгрузки", 5);
КонецПроцедуры

&НаСервереБезКонтекста
Функция ЗапуститьВыгрузкуНаСервере(ПараметрыВыгрузки)
    Возврат ФоновыеЗадания.Выполнить("МодульСерверныхПроцедур.ВыполнитьДлительнуюВыгрузку", ПараметрыВыгрузки);
КонецФункции

В этом случае процедура ВыполнитьДлительнуюВыгрузку будет работать в отдельном сеансе, который не ограничен таймаутом веб-сервера Keep-Alive.

Решение 2: Оптимизация работы с объектом обработки

Создание экземпляра УниверсальныйОбменДаннымиXML на каждой итерации цикла — крайне неэффективный путь. Каждая инициализация требует загрузки правил обмена и инициализации метаданных. Рассмотрим, как оптимизировать код, создав объект один раз перед циклом:


&НаСервере
Процедура ВыгрузитьДанныеОптимизировано()
    // Создаем объект один раз
    ОбработкаОбъект = Обработки.УниверсальныйОбменДаннымиXML.Создать();
    ОбработкаОбъект.РежимОбмена = "Выгрузка";
    ОбработкаОбъект.ИмяФайлаПравилОбмена = Объект.ПутьКФайлуПравил;
    ОбработкаОбъект.ЗагрузитьПравилаОбмена();
    
    // Получаем ссылки на параметры заранее
    ПараметрНачало = ОбработкаОбъект.ТаблицаНастройкиПараметров.Найти("ДатаНачала", "Имя");
    ПараметрКонец = ОбработкаОбъект.ТастройкаПараметров.Найти("ДатаОкончания", "Имя");
    
    Пока ТекКодПервый <= Объект.МаксимальныйНомер Цикл
        // Меняем только значения параметров и имя файла
        ОбработкаОбъект.ИмяФайлаОбмена = ПолучитьИмяФайла(ТекКодПервый);
        УстановитьЗначенияПараметров(ОбработкаОбъект, ТекКодПервый);
        
        // Выполняем выгрузку порции данных
        ОбработкаОбъект.ВыполнитьВыгрузку();
        
        // Инкремент счетчиков
        ТекКодПервый = ТекКодПервый + Объект.ШагПакета;
    КонецЦикла;
КонецПроцедуры

Такой подход значительно снижает нагрузку на память (RAM) и уменьшает общее время выполнения, что косвенно снижает вероятность возникновения таймаута.

Решение 3: Настройка инфраструктуры (веб-сервер)

Если перевод на фоновые задания по каким-то причинам невозможен, следует проанализировать настройки веб-сервера. Выясним причину обрыва соединения в логах. Для IIS следует увеличить значения параметров Connection Timeout и настройки CGI/FastCGI Timeout. Для Apache обратите внимание на директивы Timeout и KeepAliveTimeout.

Важный момент: Если между клиентом и сервером стоит прокси-сервер (например, при работе через облако или Nginx), таймауты нужно увеличивать и на нем (параметры proxy_read_timeout в Nginx).

Диагностика с помощью Технологического журнала

Чтобы окончательно убедиться, что происходит именно повторный вызов, рекомендуется настроить Технологический журнал (ТЖ). Нас интересуют события CALL (вызовы серверных методов) и EXCP (исключения).

Если в ТЖ вы видите два события CALL для одной и той же процедуры с небольшим интервалом, при этом первое не завершилось событием RETN (возврат), значит, имеет место дублирование запроса со стороны сетевой инфраструктуры.

Резюмируем:

  1. Проблема «зацикливания» процедуры обычно внешняя и связана с HTTP-таймаутами.
  2. Лучший способ защиты — использование фоновых заданий.
  3. Не создавайте тяжелые объекты (обработки, COM-соединения) внутри циклов.
  4. Проверьте лимиты памяти для процессов rphost в консоли администрирования кластера 1С.
← На главную