При интеграции двух информационных баз 1С с помощью внешнего соединения часто возникает проблема производительности: инициализация COM-объекта V83.COMConnector и авторизация в целевой базе занимают от нескольких секунд до десятков секунд. Если такой вызов выполняется интерактивно из формы или при проведении документов, пользователи сталкиваются с сильными задержками. Рассмотрим подробно, где можно сохранить открытое соединение в обычном приложении, с какими платформенными ограничениями придется столкнуться и как правильно организовать архитектуру обмена.
В толстом клиенте обычного приложения наиболее прямолинейным способом повторного использования является размещение объекта в глобальной переменной. Память толстого клиента принадлежит процессу 1cv8.exe, и созданный COM-объект будет жить на протяжении всего сеанса работы пользователя.
Разберем по шагам, как правильно реализовать такое хранилище. Главная опасность долгоживущего соединения — его внезапное отключение (например, удаленный сервер перезагрузился или сбросил неактивную сессию по таймауту). Поэтому перед выполнением операций необходимо делать проверку активности соединения (heartbeat) через блок Попытка ... Исключение.
Посмотрим на пример организации глобальной переменной и функции получения соединения в МодулеОбычногоПриложения:
Перем глCOMСоединение Экспорт;
Функция ПолучитьCOMСоединение() Экспорт
Если глCOMСоединение <> Неопределено Тогда
Попытка
// Проверяем доступность базы через легкое обращение к метаданным
ИмяБазы = глCOMСоединение.Метаданные.Имя;
Возврат глCOMСоединение;
Исключение
// Соединение разорвано или "протухло", очищаем переменную
глCOMСоединение = Неопределено;
КонецПопытки;
КонецЕсли;
Попытка
Коннектор = Новый COMОбъект("V83.COMConnector");
СтрокаПодключения = "Srvr=""Server1C"";Ref=""TargetBase"";Usr=""ExchangeUser"";Pwd=""SecretPass"";";
глCOMСоединение = Коннектор.Connect(СтрокаПодключения);
Исключение
глCOMСоединение = Неопределено;
ВызватьИсключение "Ошибка подключения к удаленной базе: " + ОписаниеОшибки();
КонецПопытки;
Возврат глCOMСоединение;
КонецФункции
Важное правило работы с памятью: каждый объект, полученный из внешнего соединения (выборка, ссылка или таблица значений), удерживает память на стороне удаленного сервера. При использовании постоянного соединения обязательно зануляйте промежуточные переменные (Объект = Неопределено;) сразу после окончания их обработки, иначе возникнут серьезные утечки оперативной памяти.
Проанализируем распространенные заблуждения касательно того, где еще можно разместить COM-объект:
COMОбъект напрямую или внутри структуры/массива в параметре сеанса невозможно. Платформа 1С требует, чтобы все значения параметров сеанса поддерживали XDTO-сериализацию. Указатели COM не сериализуются, при попытке записи возникнет ошибка несоответствия типов.1cv8.exe или серверного rphost). Передать активный COM-указатель между клиентом и сервером или выгрузить во временное хранилище для другого потока нельзя.Еще один способ кэширования — создание функции подключения в общем модуле со свойством Повторное использование возвращаемых значений (на время сеанса или на время вызова).
Выясним особенности этого механизма:
ОсвежитьПовторноИспользуемыеЗначения().Попытка вызывать внешнее соединение в обработчике ОбработкаПроведения или из модуля формы документа несет критические риски для стабильности базы данных:
Оптимальным решением без синхронных задержек пользователей является разделение фиксации данных и их отправки во внешнюю систему. Рассмотрим, как это сделать по шагам:
ОчередьИсходящихСообщений с измерением Документ (или СсылкаНаОбъект) и ресурсом ДатаРегистрации.Посмотрим на пример логики регламентного задания, обрабатывающего очередь пакетами:
Процедура ВыполнитьОтправкуОчереди() Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 50
| Очередь.Документ КАК Документ
|ИЗ
| РегистрСведений.ОчередьИсходящихСообщений КАК Очередь
|УПОРЯДОЧИТЬ ПО
| Очередь.ДатаРегистрации ВОЗР";
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
Возврат;
КонецЕсли;
// Инициализируем соединение один раз на весь пакет
Соединение = ПолучитьCOMСоединение();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
Попытка
// Передаем данные во внешнюю базу через Соединение
// ... логика передачи ...
// Удаляем запись из очереди после успешной передачи
НаборЗаписей = РегистрыСведений.ОчередьИсходящихСообщений.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Документ.Установить(Выборка.Документ);
НаборЗаписей.Записать();
Исключение
// Фиксируем ошибку и прерываем пачку, чтобы не терять данные
ЗаписьЖурналаРегистрации("Интеграция COM", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
Прервать;
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Если отказаться от синхронных вызовов невозможно, время на инициализацию можно существенно сократить на уровне операционной системы, настроив пул объектов COM+:
dcomcnfg) на сервере Windows.comcntr.dll из каталога установленной платформы 1С.dllhost.exe, ускоряя повторные подключения и изолируя адресное пространство основного процесса 1С.В долгосрочной перспективе рекомендуется полностью заменить COM-технологию на стандартные веб-протоколы (HTTP / REST или SOAP / WS):