Как быстро вызвать функцию или получить данные из другой конфигурации 1С без её изменения?

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

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

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

Решение 1. Оптимизация COM-соединения с использованием пула соединений (Connection Pooling)

Основная причина низкой скорости работы через COM-соединение — это не сам процесс передачи данных или выполнение запроса, а длительная инициализация сеанса. При каждом вызове метода Connect платформа выполняет авторизацию пользователя, инициализирует параметры сеанса, запускает модули системы и проверяет лицензии. На это уходит от 1.5 до 5 секунд, что критично при многопоточной работе.

Чтобы избежать этих затрат времени, мы должны использовать встроенный механизм пулирования соединений, предоставляемый объектом V83.COMConnector. Пул позволяет держать несколько авторизованных сеансов в активном (но спящем) состоянии и мгновенно использовать их при поступлении запроса.

Разберем, как настроить пул соединений программно на стороне вызывающей системы:


// Создаем объект коннектора
Коннектор = Новый COMОбъект("V83.COMConnector");

// Настраиваем параметры пула соединений
// Максимальное количество удерживаемых в пуле неактивных соединений
Коннектор.PoolCapacity = 10; 

// Время жизни неактивного соединения в пуле (в секундах)
Коннектор.PoolTimeout = 60; 

// Максимальное количество одновременно открытых соединений
Коннектор.MaxConnections = 20; 

// Выполняем подключение. При первом вызове произойдет полноценная авторизация.
// Все последующие вызовы с аналогичной строкой подключения будут брать сеанс из пула мгновенно!
СтрокаПодключения = "Srvr=""Server1C"";Ref=""BaseSource"";Usr=""IntegrationUser"";Pwd=""Password123"";";
БазаИсточник = Коннектор.Connect(СтрокаПодключения);

Для надежной многопоточной работы (например, под нагрузкой в 20 параллельных потоков из Java-приложения) рекомендуется настроить библиотеку comcntr.dll в операционной системе Windows через консоль «Службы компонентов» (COM+). Создание выделенного серверного приложения COM+ изолирует процесс работы с 1С в отдельном пуле dllhost.exe, предотвращая утечки памяти и зависания основного рабочего процесса.

Решение 2. Выполнение произвольного кода через внешние обработки (.epf) без изменения конфигурации

Что делать, если в базе-источнике нет готовой экспортной функции для получения нужных данных, а менять конфигурацию или добавлять расширения нельзя? Мы можем использовать механизм внешних обработок, вызываемых через COM-соединение.

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

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

  1. Создадим внешнюю обработку и сохраним её на сервере под именем DataRetriever.epf.
  2. В модуле объекта обработки напишем экспортную функцию, которая выполняет запрос и подготавливает структуру данных:

Функция ПолучитьОстаткиИЦеныНоменклатуры(ПараметрыЗапроса) Экспорт
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Цены.Номенклатура.Код КАК Код,
        |   Цены.Цена КАК Цена,
        |   ЕСТЬNULL(Остатки.ВНаличииОстаток, 0) КАК Остаток
        |ИЗ
        |   РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура.Код В (&Коды)) КАК Цены
        |   ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Номенклатура.Код В (&Коды)) КАК Остаток
        |   ПО Цены.Номенклатура = Остатки.Номенклатура";
        
    Запрос.УстановитьПараметр("Период", ТекущаяДата());
    Запрос.УстановитьПараметр("Коды", ПараметрыЗапроса.КодыНоменклатуры);
    
    ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
    
    // Преобразуем таблицу значений в массив структур для простой сериализации по COM
    МассивРезультатов = Новый Массив;
    Для Каждого Строка Из ТаблицаРезультата Цикл
        СтруктураСтроки = Новый Структура("Код, Цена, Остаток");
        ЗаполнитьЗначенияСвойств(СтруктураСтроки, Строка);
        МассивРезультатов.Добавить(СтруктураСтроки);
    КонецЦикла;
    
    Возврат ЗначениеВСтрокуВнутр(МассивРезультатов);
КонецФункции
  1. На стороне базы-приемника (или внешней системы) вызываем этот файл через COM-соединение:

// Подключаемся к базе-источнику (используя пул соединений из Решения 1)
БазаИсточник = Коннектор.Connect(СтрокаПодключения);

// Динамически подключаем внешнюю обработку по пути к файлу
ПутьКОбработке = "C:\Services\DataRetriever.epf";
ИмяОбработки = БазаИсточник.ВнешниеОбработки.Подключить(ПутьКОбработке);
ОбъектОбработки = БазаИсточник.ВнешниеОбработки.Создать(ИмяОбработки);

// Готовим параметры для передачи
Параметры = Новый Структура;
МассивКодов = Новый Массив;
МассивКодов.Добавить("00000001");
МассивКодов.Добавить("00000002");
Параметры.Вставить("КодыНоменклатуры", МассивКодов);

// Вызываем экспортную функцию обработки и получаем сериализованную строку данных
ДанныеВСтроке = ОбъектОбработки.ПолучитьОстаткиИЦеныНоменклатуры(Параметры);

// Восстанавливаем данные на стороне базы-приемника
МассивДанных = ЗначениеИзСтрокиВнутр(ДанныеВСтроке);

Важное замечание: При подключении внешних обработок в современных версиях платформы может сработать защита от опасных действий. Чтобы избежать ошибок, необходимо в свойствах пользователя СУБД, под которым выполняется COM-подключение, снять флажок "Защита от опасных действий" или отключить её программно при создании профиля безопасности.

Решение 3. Использование стандартного интерфейса OData (без изменения метаданных)

Многие разработчики ошибочно полагают, что использование REST API OData требует конфигурирования веб-сервисов и написания кода в базе-источнике. На самом деле стандартный интерфейс OData — это встроенная нативная возможность платформы 1С, которая запускается простым флажком при публикации базы.

Проанализируем процесс настройки стандартного интерфейса OData:

  1. Откройте базу-источник в режиме Конфигуратор.
  2. Перейдите в меню Администрирование -> Публикация на веб-сервере.
  3. На вкладке "Основные" установите флажок "Публиковать стандартный интерфейс OData".
  4. Нажмите кнопку "Опубликовать". База станет доступна по протоколу HTTP/HTTPS.

По умолчанию все объекты конфигурации могут быть закрыты для доступа через OData. Мы можем настроить состав доступных сущностей удаленно, не снимая конфигурацию с поддержки. Для этого выполним из внешней системы простой HTTP POST запрос к нашей опубликованной базе:


// Пример настройки доступности объектов OData (выполняется один раз администратором)
// Отправляем POST запрос на адрес: http://Server1C/BaseSource/odata/standard.odata/$metadata
// С телом запроса, содержащим список разрешенных таблиц (например, Справочник.Номенклатура, РегистрСведений.Цены)

После публикации внешнее Java-приложение сможет напрямую обращаться к таблицам 1С по HTTP со скоростью работы веб-сервера. Например, для получения данных о товаре с кодом "00001" достаточно выполнить GET-запрос:

GET http://Server1C/BaseSource/odata/standard.odata/Catalog_Номенклатура?$filter=Code eq '00001'

Решение 4. Прямой доступ к СУБД через SQL-представления (SQL Views)

Если требуется максимальное быстродействие при чтении данных (миллисекунды на запрос) и выполнение сложных выборок без участия сервера приложений 1С, оптимальным решением станет прямое обращение к базе данных MS SQL Server или PostgreSQL.

Однако писать прямые SQL-запросы к физическим таблицам 1С неудобно из-за нечитаемых имен полей (вроде _Fld12345_RRRef) и сложности получения виртуальных таблиц (например, "Срез последних" для периодических регистров сведений).

Рассмотрим, как решить эту проблему с помощью создания SQL-представлений (Views) непосредственно на уровне СУБД. Администратор СУБД создает представление, которое преобразует структуру 1С в удобный плоский вид:


-- Создаем SQL-представление для получения цен номенклатуры (срез последних)
CREATE VIEW v_LastPrices AS
SELECT 
    Prices.Period AS [ДатаЦены],
    Nomen.Code AS [КодТовара],
    Nomen.Description AS [Наименование],
    Prices.Price AS [Цена]
FROM 
    _InfoRg12345 AS Prices -- Физическая таблица регистра сведений Цены
INNER JOIN 
    _Reference67890 AS Nomen -- Физическая таблица справочника Номенклатура
    ON Prices._Fld12345_RRRef = Nomen._IDRRef
WHERE 
    Prices.Period = (
        SELECT MAX(SubPrices.Period) 
        FROM _InfoRg12345 AS SubPrices 
        WHERE SubPrices._Fld12345_RRRef = Prices._Fld12345_RRRef
    ) AND Prices._Active = 1;

После создания такого представления в СУБД база-получатель может подключить его с помощью стандартного объекта платформы "Внешние источники данных" или прочитать напрямую с помощью ADO/ODBC соединения:


// Чтение данных через ADO из внешней СУБД
Connection = Новый COMОбъект("ADODB.Connection");
Connection.Open("Provider=SQLOLEDB;Data Source=SQLServerName;Initial Catalog=BaseSource_DB;User ID=sa;Password=DBPassword;");

Command = Новый COMОбъект("ADODB.Command");
Command.ActiveConnection = Connection;
Command.CommandText = "SELECT * FROM v_LastPrices WHERE КодТовара = '00000001'";

RecordSet = Command.Execute();
Пока НЕ RecordSet.EOF() Цикл
    Цена = RecordSet.Fields("Цена").Value;
    Наименование = RecordSet.Fields("Наименование").Value;
    // Обработка данных...
    RecordSet.MoveNext();
КонецЦикла;
RecordSet.Close();
Connection.Close();

Преимущества прямого SQL-доступа:

Для минимизации нагрузки на рабочую базу данных рекомендуется настроить чтение SQL-представлений со вторичной реплики СУБД (например, используя технологии AlwaysOn в MS SQL Server или репликацию в PostgreSQL).

Решение 5. Создание HTTP-сервисов в Расширениях конфигурации

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

Проанализируем этот подход:

  1. Создаем новое расширение в базе-источнике.
  2. В расширении добавляем новый HTTP-сервис с шаблоном URL (например, /get_data/{Code}).
  3. В модуле HTTP-сервиса реализуем обработчик метода GET, который выполняет быстрый запрос и возвращает данные в формате JSON:

Функция ПолучитьДанныеGET(Запрос)
    
    КодТовара = Запрос.ПараметрыURL.Получить("Code");
    
    Запрос1С = Новый Запрос;
    Запрос1С.Текст = 
        "ВЫБРАТЬ
        |   Номенклатура.Наименование КАК Наименование,
        |   Цены.Цена КАК Цена
        |ИЗ
        |   Справочник.Номенклатура КАК Номенклатура
        |   ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура.Код = &Код) КАК Цены
        |   ПО (ИСТИНА)
        |ГДЕ
        |   Номенклатура.Код = &Код";
    
    Запрос1С.УстановитьПараметр("Код", КодТовара);
    Запрос1С.УстановитьПараметр("Период", ТекущаяДата());
    
    Результат = Запрос1С.Выполнить();
    Если Результат.Пустой() Тогда
        Ответ = Новый HTTPСервисОтвет(404);
        Возврат Ответ;
    КонецЕсли;
    
    Выборка = Результат.Выбрать();
    Выборка.Следующий();
    
    СтруктураДанных = Новый Структура;
    СтруктураДанных.Вставить("Name", Выборка.Наименование);
    СтруктураДанных.Вставить("Price", Выборка.Цена);
    
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, СтруктураДанных);
    СтрокаJSON = ЗаписьJSON.Закрыть();
    
    Ответ = Новый HTTPСервисОтвет(200);
    Ответ.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    Ответ.Заголовки.Вставить("Content-Type", "application/json");
    
    Возврат Ответ;
    
КонецФункции

После публикации расширения внешнее Java-приложение или вызывающая база 1С сможет запрашивать данные по сверхбыстрому протоколу HTTP, а само расширение никак не помешает последующим обновлениям типовой конфигурации.

Резюме и рекомендации по выбору технологии

Для того чтобы выбрать наиболее эффективное решение, сопоставим варианты:

← На главную