Разберем распространенную ситуацию: настроена интеграция платформы 1С с сервером очередей сообщений RabbitMQ через COM-соединение. Программа отправляет данные брокеру, в конце вызывается метод Close, а затем объекту присваивается Неопределено. Однако соединение не разрывается, и со временем на сервере RabbitMQ плодится огромное количество «мертвых» подключений, что приводит к исчерпанию ресурсов. Выясним причину такого поведения и подробно рассмотрим все способы решения этой задачи.
Чаще всего COM-обертки для интеграции 1С с RabbitMQ пишутся на языке C# с использованием официальной библиотеки платформы .NET (например, RabbitMQ.Client) и регистрируются в системе утилитой RegAsm. Истинная проблема кроется в том, как взаимодействуют платформа 1С и архитектура .NET.
Когда в коде 1С мы пишем КомОбъект = Неопределено;, мы лишь сообщаем внутреннему сборщику мусора 1С, что данная ссылка больше не используется. Однако внутри компоненты .NET сборщик мусора (Garbage Collector) работает по собственным правилам и в отдельном потоке. Он не уничтожает объекты мгновенно. В результате неуправляемые ресурсы, такие как открытые сетевые сокеты протокола AMQP, остаются висеть в памяти операционной системы, удерживая соединение с сервером кролика открытым.
Если используется внешняя компонента собственной разработки, обычных вызовов закрытия каналов бывает недостаточно. Посмотрим на решение: в код компоненты необходимо внедрить явный метод очистки памяти. Назовем его, например, Dispose или KillConnection.
Внутри этого метода необходимо принудительно разорвать связь и дать команду Garbage Collector'у собрать мусор немедленно. Рассмотрим пример реализации на стороне C#:
public void Dispose()
{
// Закрываем канал и соединение
if (channel != null && channel.IsOpen) channel.Close();
if (connection != null && connection.IsOpen) connection.Close();
// Освобождаем COM-объект
System.Runtime.InteropServices.Marshal.ReleaseComObject(this);
// Заставляем .NET собрать мусор прямо сейчас
GC.Collect();
GC.WaitForPendingFinalizers();
}
После добавления такого метода в 1С мы вызываем его перед завершением работы с объектом:
КомОбъект.Dispose();
КомОбъект = Неопределено;
Вспомним о стандартах работы с самим сервером сообщений. Отключение от RabbitMQ должно происходить в строгом порядке: сначала закрывается канал (Channel), а затем само физическое подключение (Connection). Если закрыть только соединение, канал может остаться подвешенным. Убедимся, что код вызывает правильные методы:
// Пример логики вызовов в 1С
КомОбъект.ChannelClose();
КомОбъект.ConnectionClose();
Проанализируем концептуальную ошибку, которую допускают многие программисты: открытие и закрытие соединения ради отправки одного сообщения. Это классический антипаттерн.
Подключение по протоколу AMQP — это «тяжелая» операция. Для установки соединения (handshake) требуется обмен множеством TCP-пакетов. Если система генерирует сотни сообщений в секунду, постоянное создание и уничтожение соединений положит сеть. Разберем правильный подход:
Если нет доступа к исходному коду COM-компоненты, и изменить её логику невозможно, применим стандартный архитектурный прием 1С. Всю логику общения с RabbitMQ необходимо вынести в отдельное фоновое задание.
При физическом завершении выполнения фонового задания (когда процедура модуля отработала до конца), рабочий процесс сервера 1С (rphost) жестко очищает выделенную память сеанса. В этот момент операционная система принудительно гасит все запущенные COM-объекты и связанные с ними сетевые сокеты. Таким образом, соединения не будут накапливаться.
В современных реалиях использование технологии COM (особенно на серверах под управлением ОС Linux) считается устаревшим подходом. Посмотрим на альтернативные варианты:
Management HTTP API. Обмен будет происходить через стандартный объект 1С HTTPСоединение, который предсказуемо закрывается самой платформой без зависаний.Важное примечание: Если вы выбираете Native компоненты, обратите внимание на свойство AutomaticRecoveryEnabled при настройке коннекта. Если сервер RabbitMQ внезапно уйдет в перезагрузку, компонента может начать бесконечно пытаться восстановить связь, что приведет к загрузке процессора на 100%. Обязательно учитывайте этот параметр при разработке отказоустойчивых решений.