Почему фоновые задания на выделенном сервере 1С вылетают с ошибкой «Недостаточно памяти» и как это исправить?

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

При масштабировании систем на платформе «1С:Предприятие 8.3» часто применяется практика разделения ролей серверов в кластере. Например, один сервер выделяется исключительно для обслуживания сеансов пользователей, а второй — для выполнения регламентных и тяжелых фоновых заданий. Однако при такой конфигурации разработчики и администраторы часто сталкиваются со специфическими ошибками производительности, среди которых наиболее коварной является «Недостаточно памяти для выполнения операции на один вызов» или внезапное аварийное завершение рабочих процессов rphost.

В этой статье мы подробно проанализируем, почему возникает эта проблема при переносе фоновых заданий на выделенный сервер кластера, как на это влияют параметры сеанса и модули с повторным использованием возвращаемых значений, а также рассмотрим эффективные архитектурные и административные способы её решения.

Глубокий технический анализ: почему на одном сервере всё работает, а на двух — падает?

Для того чтобы понять первопричину проблемы, разберем внутреннюю архитектуру взаимодействия рабочих процессов в многосерверном кластере 1С.

Когда вы настраиваете требования назначения функциональности (ТНФ), разделяя клиентские сеансы и фоновые задания по разным физическим серверам (например, 1S_1 для пользователей и 1S_2 для фоновых заданий), вы создаете распределенную среду. В этой среде физические процессы rphost выполняются на разных машинах, но работают в рамках единого логического кластера.

Ключевой конфликт кроется в механизме работы с Параметрами сеанса и Сервисом сеансовых данных:

  1. Сетевая сериализация данных: Все параметры сеансов физически хранятся и управляются специальным системным сервисом кластера — Сервисом сеансовых данных. По умолчанию этот сервис инициализируется и работает на центральном сервере кластера (в нашем сценарии это 1S_1).
  2. Запросы через RPC (Remote Procedure Call): Когда фоновое задание выполняется на выделенном сервере 1S_2, при каждом обращении к параметру сеанса рабочий процесс rphost на 1S_2 вынужден делать сетевой запрос к серверу 1S_1, чтобы забрать данные.
  3. Объем десериализации: Если в параметре сеанса хранится тяжелый объект (например, структура с таблицей значений на 110 000 строк, упакованная в ХранилищеЗначения), то при каждом обращении происходит полная сетевая передача этого двоичного пакета с последующей десериализацией в оперативной памяти сервера 1S_2.
  4. Фрагментация памяти: Для десериализации таблицы значений на 110К строк в памяти процесса rphost требуется выделить огромный непрерывный блок адресного пространства. Если такие обращения происходят параллельно из нескольких потоков фоновых заданий, виртуальная память процесса мгновенно фрагментируется, что приводит к ошибке выделения памяти «на один вызов», даже если суммарно физической памяти на сервере достаточно.

Влияние кэширования модулей с повторно используемыми значениями

Использование общего модуля с опцией «Повторное использование возвращаемых значений» (кэша) в данной архитектуре накладывает дополнительный негативный эффект:

При работе на одном физическом сервере (1S_1) этих проблем не возникает, так как обращение к Сервису сеансовых данных происходит локально, минуя сетевой стек и тяжелую повторную сериализацию объектов платформы.

Ниже мы подробно рассмотрим пошаговые методы исправления этой ситуации: от изменения архитектуры кода до тонкой административной настройки кластера.

Решение 1. Прямая передача данных через параметры фонового задания (Рекомендуемый подход)

Использование параметров сеанса для передачи больших объемов данных в фоновые задания является грубой архитектурной ошибкой. Гораздо эффективнее и безопаснее передавать данные напрямую в качестве параметров при старте фонового задания, либо задействовать механизм Временного хранилища.

Рассмотрим по шагам, как переписать этот механизм правильно.

Шаг 1. Помещение данных во временное хранилище и запуск задания:

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


// Код выполняется в родительском сеансе на сервере 1S_1
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("Таблица20К", ПолучитьТаблицу20К());
СтруктураДанных.Вставить("Таблица110К", ПолучитьТаблицу110К());

// Помещаем во временное хранилище с привязкой к уникальному идентификатору
АдресХранилища = ПоместитьВоВременноеХранилище(СтруктураДанных, Новый УникальныйИдентификатор);

// Передаем адрес в параметрах запуска фонового задания
ПараметрыЗапуска = Новый Массив;
ПараметрыЗапуска.Добавить(АдресХранилища);

ФоновыеЗадания.Выполнить("МойОбщийМодуль.ВыполнитьСборДанных", ПараметрыЗапуска);

Шаг 2. Получение данных внутри фонового задания:

Внутри фонового задания на сервере 1S_2 мы один раз в самом начале процедуры извлекаем данные из хранилища и работаем с ними локально в оперативной памяти.


// Код общего модуля (сервер 1S_2)
Процедура ВыполнитьСборДанных(АдресХранилища) Экспорт
    
    // Получаем структуру данных ОДИН раз
    ДанныеДляРаботы = ПолучитьИзВременногоХранилища(АдресХранилища);
    
    Если ДанныеДляРаботы = Неопределено Тогда
        ВызватьИсключение "Не удалось получить исходные данные из временного хранилища!";
    КонецЕсли;
    
    Таблица110К = ДанныеДляРаботы.Таблица110К;
    
    // Дальнейшая обработка данных происходит локально и очень быстро
    Для Каждого СтрокаТаблицы Из Таблица110К Цикл
        // Логика обработки строк
    КонецЦикла;
    
КонецПроцедуры

Этот метод исключает повторные сетевые обращения к параметрам сеанса и полностью решает проблему фрагментации памяти при десериализации.

Решение 2. Перенос тяжелых таблиц в СУБД (Базу Данных SQL)

Если к таблице на 110 тысяч строк в процессе работы фонового задания требуется обращаться сотни раз для поиска информации, держать всю эту таблицу в оперативной памяти неэффективно. Лучшее решение — делегировать операции поиска и фильтрации самому серверу СУБД, который оптимизирован для работы с такими объемами.

Рассмотрим, как реализовать этот подход:

  1. Создаем временную физическую таблицу в СУБД. Для этого в конфигураторе можно создать вспомогательный Регистр сведений или Справочник (например, Справочник.БуферДанныхФоновыхЗаданий).
  2. Перед запуском фонового задания родительский сеанс записывает 110 тысяч строк в этот справочник, помечая их уникальным идентификатором текущего пакета (GUID).
  3. В фоновое задание передается только этот GUID пакета.
  4. Внутри фонового задания вместо обращений к памяти мы используем обычные запросы 1С с отбором по нашему GUID.

Посмотрим на пример кода запроса внутри фонового задания:


// Пример быстрого точечного поиска данных через СУБД внутри фонового задания
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Буфер.Поле1,
    |	Буфер.Поле2,
    |	Буфер.Значение
    |ИЗ
    |	Справочник.БуферДанныхФоновыхЗаданий КАК Буфер
    |ГДЕ
    |	Буфер.ИдентификаторПакета = &ИдентификаторПакета
    |	И Буфер.КлючПоиска = &КлючПоиска";

Запрос.УстановитьПараметр("ИдентификаторПакета", УникальныйИдентификаторПакета);
Запрос.УстановитьПараметр("КлючПоиска", ИскомоеЗначение);

РезультатЗапроса = Запрос.Выполнить();

После завершения работы фонового задания родительский сеанс или регламентное задание очистки удаляет отработанные записи из справочника по идентификатору пакета.

Решение 3. Использование Менеджера временных таблиц

Если фоновое задание и родительский сеанс могут работать в рамках одной транзакции (или одного контекста соединения), то можно передавать данные через менеджер временных таблиц СУБД.

Для этого таблица значений помещается во временную таблицу SQL, а сам МенеджерВременныхТаблиц передается в параметры фонового задания. Это позволит выполнять пакетные запросы к данным без необходимости сериализации таблиц в память сервера приложений.

Решение 4. Перенос Сервиса сеансовых данных на выделенный сервер (Административный метод)

Если бюджет проекта не позволяет прямо сейчас вносить изменения в код конфигурации, проблему можно временно локализовать на уровне настроек кластера серверов 1С.

Мы можем принудительно заставить платформу обрабатывать Сервис сеансовых данных непосредственно на сервере фоновых заданий 1S_2, чтобы исключить сетевой оверхед.

Пошаговая настройка требований назначения функциональности в консоли кластера:

  1. Откройте утилиту администрирования кластера серверов 1С.
  2. Перейдите в свойства сервера 1S_2 и откройте раздел «Требования назначения функциональности».
  3. Добавьте новое правило:
    • Ссылка на объект: Сервис сеансовых данных (Session data service).
    • Тип требования: Назначать (Apply).
    • Имя базы данных: Укажите имя вашей информационной базы.
  4. На сервере 1S_1 добавьте зеркальное правило с типом требования «Не назначать» для этого же сервиса (либо настройте меньший приоритет).
  5. Примените требования назначения функциональности для всего кластера.

Важное предостережение: Перенос Сервиса сеансовых данных на сервер 1S_2 уберет сетевую сериализацию для фоновых заданий, но теперь обычные пользователи, работающие на 1S_1, будут совершать сетевые вызовы к 1S_2 при обращении к своим параметрам сеанса. Этот способ подходит только в том случае, если интенсивность работы пользователей с параметрами сеанса значительно ниже, чем в фоновых заданиях.

Решение 5. Ограничения лицензий и обновление платформы

Проводя аудит серверов 1С, обязательно обратите внимание на используемую лицензию и версию платформы:

← На главную