Как организовать длительное хранение и быстрый доступ к внешней компоненте Native API на сервере 1С

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

При работе с тяжелыми внешними компонентами, особенно в области криптографии или сложной обработки данных, разработчики часто сталкиваются с проблемой производительности. Если компонента, выполненная по технологии Native API, инициализируется несколько секунд, выполнять её подключение при каждом серверном вызове становится недопустимым. Однако попытки «запомнить» объект компоненты в контексте сервера стандартными средствами 1С разбиваются об архитектурные ограничения платформы. Проанализируем, почему это происходит, и рассмотрим рабочие стратегии решения этой задачи.

Выясним причину: почему Native-компоненту нельзя сохранить на сервере

Для начала разберем, почему привычные механизмы хранения данных не подходят для объектов внешних компонент. В 1С:Предприятие для сохранения состояния между вызовами сервера обычно используются Параметры сеанса или Временное хранилище. Однако эти механизмы жестко завязаны на процессе сериализации.

Сериализация — это процесс превращения объекта в поток байтов (например, в XML или внутренний формат 1С), который можно передать по сети или сохранить в базу данных. Внешние компоненты Native API представляют собой скомпилированный бинарный код (DLL), работающий напрямую с оперативной памятью. Платформа 1С не знает внутренней структуры этой памяти и не может «упаковать» живой объект C++ в байтовый поток. Именно поэтому при попытке записи компоненты в параметр сеанса возникает ошибка: объект не поддерживает сериализацию.

Проанализируем жизненный цикл в рабочем процессе rphost. На сервере любой объект живет ровно столько, сколько существует переменная, которая на него ссылается, в рамках одного серверного вызова. Как только выполнение кода возвращается на клиент, контекст серверного вызова уничтожается, счетчик ссылок обнуляется, и платформа принудительно вызывает метод Done у компоненты, выгружая её из памяти.

Разберем решение №1: Хранение компоненты на стороне клиента

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

Рассмотрим алгоритм реализации:

  1. Объявляем переменную в разделе описания переменных модуля управляемого приложения.
  2. При начале работы системы один раз инициализируем компоненту.
  3. Обращаемся к этой переменной из любого места клиентского кода.

Пример кода в модуле управляемого приложения:


Перем ГлобальнаяКомпонентаКриптографии Экспорт;

Процедура ПриНачалеРаботыСистемы()
    Если ПодключитьВнешнююКомпоненту("ОбщийМакет.КриптоБиблиотека", "Crypto", ТипВнешнейКомпоненты.Native) Тогда
        ГлобальнаяКомпонентаКриптографии = Новый("AddIn.Crypto.MainClass");
    КонецЕсли;
КонецПроцедуры

В этом случае компонента будет жить в памяти клиентского процесса ровно столько, сколько открыт сеанс пользователя. Повторная инициализация не потребуется.

Разберем решение №2: Использование COM-обертки

Интересной особенностью платформы является то, что COM-объекты ведут себя иначе. Как было замечено в обсуждении, COM-компоненты иногда удается хранить там, где Native-компоненты выдают ошибку. Это связано с механизмом маршалинга. Если компонента зарегистрирована как Out-of-Process сервер (EXE), она живет в своем собственном адресном пространстве независимо от 1С. Платформа лишь хранит ссылку на этот процесс. Если у вас есть возможность использовать COM-версию библиотеки, это может стать временным решением, однако технология COM считается устаревшей и плохо масштабируемой в среде Linux.

Разберем решение №3: Создание внешнего микросервиса (Sidecar)

Если работа с компонентой должна происходить строго на сервере (например, регламентное задание по шифрованию архивов), и клиентское хранение невозможно, единственным надежным архитектурным решением будет вынос компоненты за пределы процесса rphost. Рассмотрим создание небольшого прокси-приложения (сервиса), которое будет держать компоненту постоянно загруженной.

Для взаимодействия 1С с таким сервисом можно использовать несколько протоколов:

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

Анализ возможности использования фоновых заданий

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


Процедура ВыполнитьОбработкуКриптографии() Экспорт
    // Инициализация один раз при старте задания
    ПодключитьВнешнююКомпоненту(...);
    Компонента = Новый(...);
    
    Пока Истина Цикл
        // Проверяем наличие заданий в базе данных
        Задание = ПолучитьНовоеЗаданиеИзОчереди();
        Если Задание <> Неопределено Тогда
            Результат = Компонента.Шифровать(Задание.Данные);
            СохранитьРезультат(Задание, Результат);
        КонецЕсли;
        
        // Пауза, чтобы не нагружать процессор
        Приостановить(1); 
    КонецЦикла;
КонецПроцедуры

Важное ограничение: Фоновое задание не сможет «отдать» готовый объект компоненты в другой сеанс. Оно может только принять данные, обработать их внутри себя и вернуть результат через хранилище. Это хороший вариант для асинхронных задач, но он не решает проблему, если вам нужен немедленный ответ в пользовательском сеансе здесь и сейчас.

Технические нюансы Native API и потокобезопасность

Если вы решите реализовать совместное использование одной компоненты разными сеансами (через внешний сервис или статические переменные в самой DLL), необходимо учитывать вопрос Thread-safety (потокобезопасности). Большинство криптографических библиотек на C++ не рассчитаны на одновременное обращение из разных потоков к одному экземпляру объекта. Если два пользователя одновременно попытаются вызвать метод шифрования, это может привести к повреждению памяти и аварийному завершению процесса rphost или вашего сервиса. При реализации пула обращений обязательно используйте механизмы блокировок (мьютексы) или создавайте пул из нескольких инициализированных экземпляров компоненты.

Подведем итог: Прямого способа «заставить» 1С хранить Native-компоненту на сервере между вызовами не существует из-за ограничений сериализации. Наиболее правильным путем является либо перенос компоненты на клиент (глобальная переменная модуля приложения), либо создание внешнего сервиса-обертки, который возьмет на себя управление жизненным циклом тяжелой библиотеки.

← На главную