В практике разработки на платформе 1С:Предприятие часто возникает необходимость расширить стандартные возможности системы за счет стороннего софта. Это могут быть консольные утилиты для конвертации файлов, работы с архивами, отправки специфических сетевых запросов или криптографические модули. Самым простым и соблазнительным решением кажется упаковка стороннего исполняемого файла (весом в 20 МБ и более) прямо в макет двоичных данных внешней обработки (файлы .epf или .erf). На первый взгляд, это гарантирует полную автономность: обработка сама распакует всё необходимое при первом запуске. Но так ли это «комильфо» с точки зрения архитектуры и производительности сервера? Давайте подробно разберем этот вопрос, проанализируем поведение платформы и рассмотрим наиболее надежные варианты реализации.
Для начала выясним, как платформа 1С:Предприятие ведет себя при работе с внешними обработками большого объема. Когда мы вызываем метод ВнешниеОбработки.Создать(ПутьКФайлу) или используем механизмы БСП для подключения дополнительных обработок, платформа вынуждена прочитать весь файл с диска или из базы данных (если обработка хранится в справочнике).
Важный нюанс работы с памятью: даже если сам двоичный макет инициализируется как полноценный объект встроенного языка ДвоичныеДанные только при непосредственном вызове метода ПолучитьМакет(), сырой поток байтов всей обработки всё равно считывается в буфер памяти рабочего процесса rphost при ее регистрации в сеансе. Если внешняя обработка весит 22 МБ, то при каждом её создании сервер выделяет соответствующий объем оперативной памяти под хранение структуры обработки.
Представим сценарий, когда данная обработка запускается в рамках регламентного задания каждые несколько минут. Постоянное пересоздание объекта весом более 20 МБ в памяти приведет к следующим негативным последствиям:
rphost, что на высоконагруженных серверах может приводить к преждевременному перезапуску рабочих процессов по превышению лимитов памяти.Рассмотрим сценарий, когда мы все же решили хранить утилиту в макете. При первом запуске обработка проверяет наличие исполняемого файла на диске и, если его нет, извлекает его из макета. Но что произойдет, когда вам потребуется обновить версию сторонней утилиты?
Если в коде реализована простая проверка существования файла на диске вроде Файл.Существует(), то после обновления макета в коде 1С старая версия утилиты на сервере так и останется нетронутой. Платформа обнаружит ранее распакованный файл старой версии и продолжит использовать именно его. Чтобы этого избежать, нам придется реализовывать дополнительные проверки.
Проанализируем пример кода, который решает эту проблему за счет контроля версии через имя файла или проверку хэш-суммы MD5:
// Пример контроля версии утилиты при распаковке из макета
ИмяФайлаУтилиты = "my_utility_v1_2.exe"; // Явно указываем версию в имени
ПутьККаталогу = "C:\Services\ExternalUtilities\";
ПолныйПутьКУтилите = ПутьККаталогу + ИмяФайлаУтилиты;
ФайлНаДиске = Новый Файл(ПолныйПутьКУтилите);
Если Не ФайлНаДиске.Существует() Тогда
// Если файла текущей версии нет, извлекаем его из макета
МакетДвоичныеДанные = ЭтотОбъект.ПолучитьМакет("ИсполняемыйФайл");
МакетДвоичныеДанные.Записать(ПолныйПутьКУтилите);
// Опционально: удаляем старые версии утилиты из каталога, чтобы не копить мусор
// (для этого можно перебрать файлы по маске "my_utility_*.exe")
КонецЕсли;
Если утилита запускается в многопоточном режиме или одновременно несколькими фоновыми заданиями, попытка перезаписать исполняемый файл, который в данный момент запущен другим сеансом, приведет к критической ошибке совместного доступа (файл заблокирован операционной системой). Для решения этой проблемы рекомендуется распаковывать утилиты в уникальные временные папки для каждого сеанса, используя метод ПолучитьИмяВременногоФайла().
Современные версии платформы 1С:Предприятие 8.3 предъявляют жесткие требования к безопасности выполнения кода во внешних обработках. По умолчанию внешние обработки запускаются в безопасном режиме. В этом режиме платформой полностью заблокирован запуск любых внешних приложений через методы ЗапуститьПриложение() или КомандаСистемы().
Для корректной работы вашей утилиты администратору системы потребуется настроить **профили безопасности** в кластере серверов 1С и явно разрешить запуск конкретного исполняемого файла из определенного каталога. Без этой настройки вы столкнетесь с ошибками прав доступа на стороне платформы 1С, даже если у учетной записи службы usr1cv8 в операционной системе есть полные права.
Если серверная часть 1С работает на операционной системе Linux, хранение и запуск исполняемых файлов из макетов сталкивается с дополнительными трудностями:
x). Попытка вызвать команду запуска завершится ошибкой «Доступ запрещен». Разработчику необходимо программно выдать права на исполнение перед запуском..exe, скомпилированный под Windows, физически не запустится на Linux без использования Wine, что крайне не рекомендуется для промышленных серверов. Соответственно, в макетах придется хранить разные версии утилит под разные ОС и программно определять текущую операционную систему.Посмотрим, как программно установить права на выполнение файла в среде Linux из кода 1С:
// Установка прав на исполнение для Linux систем
СистемнаяИнформация = Новый СистемнаяИнформация;
Если СистемнаяИнформация.ТипПлатформы = ТипПлатформыДокументаВызова.Linux Тогда
КомандаУстановкиПрав = "chmod +x " + ПолныйПутьКУтилите;
ЗапуститьПриложение(КомандаУстановкиПрав, , Истина);
КонецЕсли;
Чтобы не раздувать размер файлов внешних обработок и гарантировать стабильную работу системы, рекомендуется отказаться от хранения тяжелых бинарников внутри файлов .epf. Рассмотрим три наиболее правильных альтернативных подхода.
При этом подходе сама внешняя обработка остается легкой (около 1–2 МБ), а файл утилиты загружается один раз в режиме «1С:Предприятие» в специальный справочник (например, стандартный справочник БСП «Файлы» или разработанный вами справочник с реквизитом типа ХранилищеЗначения).
При запуске регламентного задания обработка считывает двоичные данные из базы данных, сохраняет их во временный каталог и запускает. Это позволяет обновлять сторонний софт простым переприкреплением файла в режиме пользователя, без необходимости монопольного изменения кода обработки или конфигурации.
Разберем пример реализации извлечения утилиты из хранилища значений:
// Пример получения бинарного файла из базы данных
Функция ПолучитьУтилитуИзБазы(ИмяУтилиты)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ХранилищеУтилит.ФайлХранилище КАК ФайлХранилище
|ИЗ
| Справочник.ХранилищеУтилит КАК ХранилищеУтилит
|ГДЕ
| ХранилищеУтилит.Наименование = &ИмяУтилиты";
Запрос.УстановитьПараметр("ИмяУтилиты", ИмяУтилиты);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.ФайлХранилище.Получить(); // Возвращает ДвоичныеДанные
КонецЕсли;
Возврат Неопределено;
КонецФункции
Если логику внешней утилиты можно переписать или обернуть в динамическую библиотеку (.dll под Windows и .so под Linux), то самым надежным решением станет создание внешней компоненты по технологии Native API. Платформа 1С умеет штатно работать с такими компонентами, автоматически кэшируя их на сервере, управляя их жизненным циклом и решая проблемы с правами доступа и кроссплатформенностью в рамках единого ZIP-архива компоненты.
Если у вас крупная инфраструктура, правильным решением будет вынести все сторонние утилиты на внутренний веб-сервер организации. При запуске обработка проверяет локальное наличие файла, а при его отсутствии — отправляет HTTP-запрос и скачивает необходимую версию утилиты по защищенному протоколу HTTPS. Это минимизирует объем базы данных 1С и гарантирует централизованное обновление вспомогательного софта силами системных администраторов.