Как сохранить COM-объект на сервере между серверными вызовами и ускорить работу системы

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

В разработке на платформе 1С:Предприятие 8.3 (особенно в управляемых формах) программисты часто сталкиваются с проблемой "короткой жизни" серверного контекста. Все переменные, созданные в рамках одного серверного вызова &НаСервере, уничтожаются сразу после завершения процедуры. Если ваша задача требует взаимодействия с внешними системами через COM-объекты (например, V83.COMConnector, Excel или специализированные драйверы), это становится серьезным препятствием. Инициализация такого объекта может занимать несколько секунд, и повторять эту операцию при каждом нажатии кнопки пользователем — недопустимая роскошь.

В данной статье мы разберем, как "обмануть" систему и заставить COM-объект оставаться в памяти сервера, проанализируем риски каждого метода и выберем наиболее стабильное решение.

Причина проблемы: архитектура серверного кластера

Прежде чем переходить к коду, проанализируем ситуацию. Сервер 1С работает как совокупность рабочих процессов rphost. Когда клиент делает запрос к серверу, он попадает в один из этих процессов. Как только выполнение кода завершается, 1С очищает память, выделенную под локальные переменные сеанса, чтобы освободить ресурсы для других пользователей. COM-объект является внешним по отношению к 1С, но ссылка на него хранится именно в этой очищаемой памяти.

Способ 1: Использование временного хранилища с привязкой к форме

Рассмотрим вариант, предложенный в обсуждении, который позволяет хранить ссылку на объект в течение жизни конкретной формы. Основная хитрость здесь заключается в использовании УникальныйИдентификатор формы в качестве ключа для ПоместитьВоВременноеХранилище.

Разберем пример кода, реализующий этот механизм:


&НаСервере
Функция ПолучитьСоединениеСВнешнейСистемой(НужноНовоеСоединение = Ложь)
    РезультатСоединения = Неопределено;
    
    // Пытаемся извлечь структуру с объектом из хранилища
    Если Не ПустаяСтрока(Объект.АдресВХранилище) Тогда
        ДанныеИзХранилища = ПолучитьИзВременногоХранилища(Объект.АдресВХранилище);
        Если ТипЗнч(ДанныеИзХранилища) = Тип("Структура") Тогда
            РезультатСоединения = ДанныеИзХранилища.COMОбъект;
        КонецЕсли;
    КонецЕсли;        
    
    // Если объекта нет или принудительно затребовано новое соединение
    Если (РезультатСоединения = Неопределено) Или НужноНовоеСоединение Тогда
        Попытка
            Connector = Новый COMОбъект("V83.COMConnector");
            ПараметрыСвязи = "Srvr='ServerName';Ref='BaseName';Usr='Login';Pwd='Password';";
            РезультатСоединения = Connector.Connect(ПараметрыСвязи);
            
            // Сохраняем объект в структуру и помещаем в хранилище
            Контейнер = Новый Структура("COMОбъект", РезультатСоединения);
            // Важно: привязываем к уникальному идентификатору формы
            Объект.АдресВХранилище = ПоместитьВоВременноеХранилище(Контейнер, ЭтаФорма.УникальныйИдентификатор);
        Исключение
            ЗаписьЖурналаРегистрации("Ошибка подключения", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
            ВызватьИсключение "Не удалось установить COM-соединение!";
        КонецПопытки;
    КонецЕсли;
    
    Возврат РезультатСоединения;
КонецФункции

Важный нюанс: 1С не умеет по-настоящему сериализовать COM-объект в строку. Она сохраняет во временном хранилище лишь IDispatch — указатель на интерфейс объекта в памяти текущего процесса rphost. Если ваш кластер состоит из нескольких рабочих процессов или серверов, и следующий вызов попадет на другой процесс, вы получите ошибку "Объект не найден", так как в памяти другого процесса по этому адресу ничего нет.

Способ 2: Общий модуль с повторным использованием значений

Это один из самых элегантных способов кэширования на стороне сервера. Мы создаем общий модуль, устанавливаем у него свойство Повторное использование возвращаемых значений в значение "На время сеанса" или "На время вызова".

Рассмотрим, как это работает:

  1. Создаем модуль КэшВнешнихСоединенийСервер.
  2. В свойствах модуля выбираем Повторное использование: На время сеанса.
  3. Пишем функцию получения объекта:

Функция ПолучитьАктивныйCOMОбъект() Экспорт
    // Этот код выполнится один раз за 20 минут (по умолчанию)
    // или пока память процесса не будет переполнена
    Connector = Новый COMОбъект("V83.COMConnector");
    Возврат Connector.Connect("СтрокаСоединения");
КонецФункции

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

Способ 3: Асинхронная обработка через очередь и регламентные задания

Если COM-объект крайне нестабилен или его инициализация критически замедляет работу интерфейса, мы можем применить архитектурный паттерн "Очередь задач".

Выясним причину, почему это эффективно: мы разделяем пользовательский интерфейс и тяжелую логику внешнего объекта. Пользователь лишь фиксирует намерение (записывает данные в регистр), а специально выделенный процесс эти данные обрабатывает.

Разберем шаги реализации:

  1. Создаем Регистр Сведений ОчередьЗадачCOM с полями: ПараметрыЗапуска, Статус, Результат.
  2. Настраиваем Регламентное задание, которое запускается раз в несколько секунд.
  3. В процедуре регламентного задания создаем бесконечный цикл (с разумным выходом), который инициализирует COM-объект один раз при старте и обрабатывает все записи из регистра.

Процедура ОбработкаОчередиCOM() Экспорт
    // Инициализируем объект один раз
    ВнешнийОбъект = Новый COMОбъект("SomeHeavyLibrary.Application");
    
    Пока Истина Цикл
        ЗаписьОчереди = ВыбратьПервуюСвободнуюЗапись();
        Если ЗаписьОчереди = Неопределено Тогда
            Приостановить(2); // Ждем новые задачи, не нагружая процессор
            Продолжить;
        КонецЕсли;
        
        // Выполняем действия с объектом
        Результат = ВнешнийОбъект.DoWork(ЗаписьОчереди.Параметры);
        СохранитьРезультат(ЗаписьОчереди, Результат);
    КонецЦикла;
КонецПроцедуры

Этот метод гарантирует, что COM-объект "висит" в памяти столько, сколько живет фоновое задание. Это самый стабильный вариант для высоконагруженных систем.

Способ 4: Регистрация в COM+ (Component Services)

Если вы опытный администратор или разработчик, вы можете вынести инициализацию объекта за пределы 1С. Зарегистрируйте вашу DLL в службах компонентов Windows (COM+). В настройках приложения COM+ укажите тип активации Server Application. Это заставит систему запускать объект в отдельном процессе dllhost.exe. Теперь, даже если 1С завершит свой процесс или сеанс, сам COM-объект может оставаться активным в памяти Windows, ожидая новых подключений.

Итоговые рекомендации

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

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

← На главную