Как организовать совместное использование подключения к внешней базе для всех сеансов в 1С и почему временное хранилище не работает

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

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

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

Почему логика работы через Временное хранилище ошибочна

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


мСоединениеБП = ПолучитьИзВременногоХранилища(Константы.СтрокаХранилища.Получить());
Если Строка(мСоединениеБП) <> "COMОбъект" Тогда
    Константы.СтрокаХранилища.Установить(ПоместитьВоВременноеХранилище(rsОбщийМодульОбмен.ПолучитьПодключениеКСервернойБД(), Новый УникальныйИдентификатор));
    мСоединениеБП = ПолучитьИзВременногоХранилища(Константы.СтрокаХранилища.Получить());
КонецЕсли;

Этот алгоритм не будет работать по двум фундаментальным причинам, заложенным в архитектуру платформы 1С:Предприятие:

  1. Изоляция сеансов во Временном хранилище. Временное хранилище жестко привязано к конкретному сеансу пользователя (и даже к конкретной форме или фоновому заданию внутри сеанса). Когда мы вызываем метод ПоместитьВоВременноеХранилище, платформа регистрирует переданный объект во временном реестре текущего сеанса и возвращает уникальный адрес (строку вида "e1cib/tempstorage/..."). Если мы запишем эту строку-адрес в константу или общую базу данных, другие пользователи смогут прочитать саму строку. Но при попытке вызвать ПолучитьИзВременногоХранилища в другом сеансе платформа вернет Неопределено, так как у другого сеанса нет физического доступа к памяти первого сеанса.
  2. Несериализуемость COM-объектов. Для того чтобы поместить любой объект во временное хранилище (или базу данных), платформа должна его сериализовать — то есть преобразовать в последовательность байтов (XML или двоичный поток). Объект типа COMОбъект (включая V83.COMConnector) является указателем операционной системы на конкретные области в оперативной памяти и запущенные внешние процессы (например, dllhost.exe или rphost.exe). Такой объект физически невозможно сериализовать. Платформа 1С выдаст ошибку при попытке поместить «живое» COM-соединение во временное хранилище или базу данных.

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

---

Решение 1. Использование встроенного пула соединений COM-соединителя

Прежде чем переписывать всю архитектуру обмена, проанализируем встроенные возможности самой платформы 1С. Мало кто знает, что объект V83.COMConnector умеет самостоятельно управлять повторным использованием физических подключений на уровне операционной системы. Этот механизм называется пулом соединений (Connection Pooling).

Вместо того чтобы изобретать велосипед с передачей объекта, мы можем настроить параметры пула при инициализации коннектора на сервере. Разберем пример реализации:


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

// Настраиваем параметры пула соединений
Коннектор.PoolCapacity = 10;     // Максимальное количество удерживаемых открытых соединений в пуле
Коннектор.PoolTimeout = 60;      // Время жизни неактивного соединения в пуле в секундах
Коннектор.MaxConnections = 20;   // Максимально возможное число одновременно открытых соединений

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

---

Решение 2. Паттерн «Медиатор» (Асинхронное выполнение через очередь задач)

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

Проанализируем схему работы этого паттерна по шагам:

  1. Создаем в конфигурации регистр сведений ОчередьЗадачКВнешнейБазе со следующей структурой:
    • Измерения: ИдентификаторЗадачи (тип УникальныйИдентификатор).
    • Ресурсы: Состояние (тип ПеречислениеСсылка.СостоянияЗадач: ВОчереди, Выполняется, Выполнена, Ошибка), Результат (тип ХранилищеЗначения).
    • Реквизиты: ПараметрыЗадачи (тип ХранилищеЗначения), ТекстОшибки (тип Строка).
  2. Когда пользователю в сеансе необходимо получить данные или отправить документ во внешнюю базу, он не создает COM-объект. Он формирует структуру параметров, упаковывает её в ХранилищеЗначения и записывает новую строку в регистр ОчередьЗадачКВнешнейБазе со статусом ВОчереди.
  3. На сервере настраивается регламентное (или фоновое) задание, которое запускается с высокой периодичностью (например, каждые 5-10 секунд) под одной системной учетной записью. Это задание выполняет следующие действия:
    • Считывает все записи из регистра со статусом ВОчереди.
    • Инициализирует ровно одно COM-соединение.
    • Последовательно (или параллельно в несколько потоков, если настроено) обрабатывает все задачи, отправляет данные во внешнюю базу, получает ответы.
    • Записывает результаты обратно в регистр в ресурс Результат, меняя статус задачи на Выполнена (или Ошибка).
    • Закрывает COM-соединение.
  4. Пользователь на клиенте в это время ожидает выполнения (например, через обработчик ожидания формы или механизм фонового запуска). Как только статус его задачи в регистре меняется на Выполнена, он считывает готовый результат из ресурса Результат.

Рассмотрим пример кода для записи задачи в очередь:


Функция ДобавитьЗадачуВОчередь(ИмяМетода, СтруктураПараметров) Экспорт

    НоваяЗадача = РегистрыСведений.ОчередьЗадачКВнешнейБазе.СоздатьМенеджерЗаписи();
    НоваяЗадача.ИдентификаторЗадачи = Новый УникальныйИдентификатор;
    НоваяЗадача.Состояние = Перечисления.СостоянияЗадач.ВОчереди;
    
    // Запаковываем параметры вызова в ХранилищеЗначения
    СтруктураЗапуска = Новый Структура;
    СтруктураЗапуска.Вставить("ИмяМетода", ИмяМетода);
    СтруктураЗапуска.Вставить("Параметры", СтруктураПараметров);
    
    НоваяЗадача.ПараметрыЗадачи = Новый ХранилищеЗначения(СтруктураЗапуска);
    НоваяЗадача.Записать(Истина);
    
    Возврат НоваяЗадача.ИдентификаторЗадачи;

КонецФункции

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

---

Решение 3. Современная альтернатива — HTTP-сервисы вместо COM

Использование COM-технологий в экосистеме 1С постепенно уходит в прошлое и признается устаревшим архитектурным подходом (legacy). COM жестко привязывает серверную инфраструктуру к операционной системе Windows, порождает частые проблемы с разрядностью библиотек (32-бит против 64-бит) и часто приводит к неконтролируемым утечкам оперативной памяти на сервере приложений.

Наиболее надежный, быстрый и современный способ решить задачу совместного доступа — это разработка HTTP-сервисов на стороне принимающей базы данных. Рассмотрим этот вариант подробнее:

  1. На стороне внешней базы данных (например, «1С:Бухгалтерия») создается объект конфигурации HTTP-сервис. В нем описываются шаблоны URL (например, /get_balance, /create_order) и методы (GET, POST).
  2. База публикуется на веб-сервере (IIS или Apache).
  3. На стороне базы-источника (где работают пользователи) отправка запросов происходит через стандартный объект встроенного языка HTTPСоединение.

Посмотрим на пример отправки запроса к HTTP-сервису из кода 1С:


// Настраиваем параметры подключения к веб-серверу
Сервер = "192.168.1.100";
Порт = 80;
Пользователь = "ExchangeUser";
Пароль = "SuperSecurePassword123";

// Создаем HTTP-соединение. Веб-сервер автоматически управляет пулом HTTP-сессий!
Соединение = Новый HTTPСоединение(Сервер, Порт, Пользователь, Пароль);

// Формируем запрос
Запрос = Новый HTTPЗапрос("/BP_Exchange/hs/v1/get_balance");
Запрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");

// Передаем параметры в формате JSON
ПараметрыЗапроса = Новый Структура;
ПараметрыЗапроса.Вставить("КодКонтрагента", "00-000125");

ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ПараметрыЗапроса);
СтрокаJSON = ЗаписьJSON.Закрыть();

Запрос.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);

Попытка
    Ответ = Соединение.ОтправитьДляАнализа(Запрос);
    Если Ответ.КодСостояния = 200 Тогда
        // Обрабатываем успешный ответ
        ЧтениеJSON = Новый ЧтениеJSON;
        ЧтениеJSON.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку());
        Результат = ПрочитатьJSON(ЧтениеJSON);
        ЧтениеJSON.Закрыть();
        // Дальнейшая работа с полученными данными
    Иначе
        ВызватьИсключение "Ошибка сервера: " + Ответ.КодСостояния;
    КонецЕсли;
Исключение
    ЗаписатьЛогОшибок(ОписаниеОшибки());
КонецПопытки;

Преимущества перехода на HTTP-сервисы огромны:

---

Решение 4. Использование службы компонентов Windows (COM+)

Если вы всё же вынуждены использовать классический COM (например, нет возможности опубликовать базу на веб-сервере или внести изменения в конфигурацию внешней базы), то лучшим системным решением для стабилизации работы будет вынос COM-коннектора в выделенное приложение **COM+** в Windows.

Разберем по шагам, как настроить эту схему:

  1. На сервере приложений Windows откройте оснастку Службы компонентов (Component Services), введя команду dcomcnfg в строке выполнения.
  2. Перейдите в ветку Службы компонентов -> Компьютеры -> Мой компьютер -> Приложения COM+.
  3. Нажмите правой кнопкой мыши по разделу и выберите Создать -> Приложение.
  4. В мастере создания выберите Создать новое пустое приложение. Назовите его, например, 1C_COMConnector.
  5. На шаге выбора режима активации обязательно выберите пункт Серверное приложение (Server Application). Это заставит систему запускать COM-объект в выделенном системном процессе dllhost.exe, а не внутри рабочего процесса 1С rphost.exe.
  6. На шаге настройки удостоверения (Identity) укажите выделенного системного пользователя Windows, от имени которого будет запускаться процесс (у этого пользователя должны быть достаточные права на папки 1С и реестр).
  7. После создания приложения перейдите в его ветку, найдите папку Компоненты (Components), нажмите Создать -> Компонент и зарегистрируйте библиотеку внешнего соединения 1С comcntr.dll (она находится в каталоге установленной платформы 1С, например, C:\Program Files\1cv8\8.3.XX.XXXX\bin\comcntr.dll).

Что мы получаем в результате этой настройки:

Каждое из рассмотренных решений имеет свои плюсы и минусы. Для быстрых доработок «на коленке» отлично подойдет настройка встроенного PoolCapacity для коннектора. Для крупных промышленных систем с повышенными требованиями к отказоустойчивости рекомендуется использовать асинхронный паттерн очереди задач либо полностью переходить на современные HTTP-сервисы.

← На главную