При разработке высоконагруженных многопоточных систем на платформе 1С:Предприятие программисты часто сталкиваются с необходимостью организовать быстрый межпроцессный обмен (Inter-Process Communication, IPC). Типичный сценарий: несколько параллельных фоновых заданий выполняют тяжелые расчеты или обработку данных и должны отправлять логи, метрики или промежуточные статусы в основной поток в режиме реального времени.
Стандартные механизмы платформы, такие как запись в СУБД (через регистры сведений или ХранилищеОбщихНастроек), создают колоссальную нагрузку на дисковую подсистему и базу данных, вызывая транзакционные блокировки и существенно замедляя работу. В этой статье мы подробно разберем, как передавать данные между фоновыми заданиями напрямую через оперативную память сервера приложений, минуя диски и СУБД.
Для начала проанализируем, почему привычные инструменты платформы оказываются неэффективными для высокопроизводительного обмена:
ПоместитьВоВременноеХранилище): Многие разработчики ошибочно полагают, что временное хранилище работает исключительно в оперативной памяти. Согласно официальной документации фирмы «1С», данные временного хранилища (сеансовые данные) принудительно сбрасываются на диск сервера приложений в каталог сеансовых данных. Кроме того, данные, помещенные в хранилище внутри фонового задания, становятся доступны основному сеансу только после полного завершения работы этого фонового задания, что делает невозможным мониторинг в реальном времени.Разберем работающие альтернативные архитектурные решения.
Наиболее эффективным встроенным инструментом для передачи данных «снизу вверх» (от фонового задания к запустившему его сеансу) является использование объекта СообщениеПользователю в связке с методом ПолучитьСообщенияПользователю.
Данные, передаваемые через этот механизм, буферизуются непосредственно в оперативной памяти рабочего процесса rphost (или менеджера кластера rmngr) и никогда не записываются на жесткий диск. Чтобы передавать сложные структуры данных, массивы или таблицы, мы применим сериализацию в формат JSON.
Рассмотрим по шагам реализацию этого паттерна.
Шаг 1. Код внутри фонового задания (отправитель данных)
Внутри выполняемого фонового задания мы упаковываем наши порции логов или результатов в структуру, сериализуем их в JSON-строку и отправляем с помощью метода Сообщить:
// Процедура фонового задания
Процедура ВыполнитьПараллельнуюОбработку(ПараметрыЗадачи) Экспорт
// Имитируем цикл обработки данных
Для Счетчик = 1 По 100 Цикл
// Формируем структуру с данными для отправки
СтруктураЛога = Новый Структура;
СтруктураЛога.Вставить("ИдентификаторПотока", ПараметрыЗадачи.Идентификатор);
СтруктураЛога.Вставить("ТекущийШаг", Счетчик);
СтруктураЛога.Вставить("Время", ТекущаяДатаСеанса());
СтруктураЛога.Вставить("Статус", "Успешно обработано");
// Сериализуем структуру в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, СтруктураЛога);
СтрокаДанных = ЗаписьJSON.Закрыть();
// Отправляем сериализованные данные "наверх" в память процесса rphost
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрокаДанных;
Сообщение.Сообщить();
// Имитация задержки работы
ФизическаяПауза(0.1);
КонецЦикла;
КонецПроцедуры
Шаг 2. Код в основном потоке (получатель данных)
Основной поток, который запустил фоновые задания, в цикле опрашивает их состояние. С помощью метода ПолучитьСообщенияПользователю(Истина) он считывает накопленные сообщения. Передача параметра Истина критически важна: она очищает буфер сообщений на сервере 1С, предотвращая переполнение оперативной памяти рабочего процесса:
// Запуск и опрос фонового задания в основном потоке
Процедура МониторингВыполненияФоновыхЗаданий()
ПараметрыЗадачи = Новый Структура("Идентификатор", "Поток_1");
ФоновоеЗадание = ФоновыеЗадания.Выполнить("МодульФоновыхЗадач.ВыполнитьПараллельнуюОбработку", ПараметрыЗадачи);
Пока Истина Цикл
// Получаем свойства для контроля состояния
СвойстваФЗ = ФоновоеЗадание.ПолучитьСвойства();
СостояниеФЗ = СвойстваФЗ.Состояние;
// Читаем новые сообщения из оперативной памяти сервера
// Параметр "Истина" очищает полученные сообщения из буфера rphost
МассивСообщений = ФоновоеЗадание.ПолучитьСообщенияПользователю(Истина);
Для Каждого Сообщение Из МассивСообщений Цикл
СтрокаJSON = Сообщение.Текст;
// Пытаемся десериализовать JSON
Попытка
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
ДанныеЛога = ПрочитатьJSON(ЧтениеJSON);
// Здесь мы можем мгновенно перенаправить данные во внешний сервис логирования
ОтправитьЛогВоВнешнююСистему(ДанныеЛога.ИдентификаторПотока, ДанныеЛога.ТекущийШаг, ДанныеЛога.Время);
Исключение
// Если пришло обычное системное сообщение (не JSON)
ЗаписатьВЖурналРегистрации("Внимание! Получено неформатированное сообщение: " + СтрокаJSON);
КонецПопытки;
КонецЦикла;
Если СостояниеФЗ <> СостояниеФоновогоЗадания.Активно Тогда
Прервать;
КонецЕсли;
// Делаем небольшую паузу между опросами
ПриостановитьПоток(0.5);
КонецЦикла;
КонецПроцедуры
Если вам требуется организовать обмен не только в направлении «снизу вверх», но и «по горизонтали» (между параллельными фоновыми заданиями), либо объемы передаваемых данных слишком велики для сериализации в JSON внутри сообщений, можно использовать технологию **RAMDrive**.
Для этого на сервере приложений 1С разворачивается специализированное ПО (например, бесплатный ImDisk Toolkit), которое выделяет часть оперативной памяти сервера ОС и монтирует ее в систему как обычный локальный диск (например, диск R:\).
Рассмотрим особенности работы с RAMDrive из 1С:
ТекстовыйДокумент, ЗаписьТекста и ЧтениеТекста.Пример записи данных фоновым заданием на RAMDrive:
ИмяФайлаЛога = "R:\logs\thread_1_" + Формат(ТекущаяДата(), "ДФ=ггггММддЧЧммсс") + ".txt";
ЗаписьТекста = Новый ЗаписьТекста(ИмяФайлаЛога, КодировкаТекста.UTF8);
ЗаписьТекста.ЗаписатьСтроку(СтрокаJSONДанных);
ЗаписьТекста.Закрыть();
Для наиболее сложных архитектурных задач, требующих гарантированной доставки, высокой скорости работы, поддержки очередей сообщений и возможности обмена данными между любыми процессами, рекомендуется использовать внешние **In-Memory Key-Value хранилища**, такие как **Redis**.
Redis разворачивается непосредственно на сервере приложений или в локальной сети. Общение фоновых заданий 1С с СУБД Redis можно реализовать следующими способами:
HTTPСоединение (данные будут передаваться в ОЗУ Redis за миллисекунды).Использование внешнего in-memory хранилища полностью разгружает рабочий процесс 1С rphost и исключает раздувание его адресного пространства при обработке гигантских массивов логирования.
Если требуется передавать информацию в реальном времени, можно развернуть внутренний легковесный HTTP-сервис внутри самой базы 1С. Каждое фоновое задание отправляет порции данных с помощью асинхронных HTTP-запросов на этот сервис.
Поскольку HTTP-сервисы обрабатываются пулом быстрых потоков в оперативной памяти rphost, при правильной настройке (без записи данных в СУБД в процессе обработки запроса) этот метод позволяет организовать быстрый и надежный транспорт данных, который можно масштабировать на несколько серверов в кластере.
Проанализируем характеристики каждого метода, чтобы выбрать оптимальный инструмент под ваши задачи:
Сообщить + JSON:
rphost.Выяснив причины медленной работы СУБД при логировании и применив любой из предложенных вариантов переноса данных в оперативную память, вы сможете поднять производительность ваших многопоточных процедур в разы, полностью ликвидировав задержки на дисковые операции ввода-вывода.