Где хранить объемные данные при работе с HTTP-сервисом в 1С?

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

Передача больших объемов данных через HTTP-сервисы в 1С — классическая задача интеграции. Часто возникает ситуация, когда внешняя система должна передать тяжелый файл (например, выгрузку товаров, логов или документов), размер которого измеряется сотнями мегабайт или даже гигабайтами. Прямая передача такого объема в рамках одного HTTP-запроса неминуемо приводит к проблемам: исчерпанию оперативной памяти на сервере, таймаутам веб-сервера (IIS/Apache) или аварийному завершению рабочего процесса rphost.

Единственным надежным выходом является разбиение данных на части (чанги) и их последовательная передача. Однако тут разработчики сталкиваются с ключевой проблемой: где накапливать эти части на стороне 1С при последовательных запросах, если стандартное временное хранилище очищается уже через 20 секунд простоя?

В этой статье мы подробно разберем причины такого поведения платформы 1С, рассмотрим классические обходные пути, предложенные разработчиками на форумах, а также проанализируем современные штатные механизмы и архитектурные паттерны, которые позволяют решить эту задачу максимально эффективно и быстро.

Выясняем причину: почему временное хранилище «теряет» данные?

Проанализируем ситуацию, с которой столкнулся автор темы. При попытке использовать метод ПоместитьВоВременноеХранилище данные бесследно исчезают, если между запросами возникает пауза более 20 секунд. Участники форума предположили, что дело в перезапуске сеансов, и они абсолютно правы.

По умолчанию HTTP-сервисы в платформе 1С работают в режиме stateless (без сохранения состояния). Платформа оптимизирует использование ресурсов сервера с помощью автоматического пула сеансов. Это означает следующее:

  1. Каждый новый входящий HTTP-запрос обрабатывается произвольным сеансом из пула. Нет никакой гарантии, что второй запрос от того же клиента попадет в тот же сеанс 1С, который обрабатывал первый запрос.
  2. Если запросов нет, платформа может мгновенно завершить «спящий» сеанс или вернуть его в общий пул для обслуживания других клиентов.
  3. Временное хранилище жестко привязано к конкретному сеансу 1С. Как только сеанс завершается или запрос уходит на другой сеанс из пула, доступ к адресу временного хранилища (вида e1cib/tempstorage/...) теряется.

Чтобы обойти это ограничение, нам нужны механизмы, которые либо свяжут запросы с одним и тем же сеансом, либо позволят хранить состояние (данные) вне сеанса 1С. Рассмотрим все возможные решения по шагам.

Решение 1. Официальный механизм платформы — Ручное управление сеансами (IBSession)

Начиная с версии платформы 1С 8.3.9, разработчики добавили штатную возможность сохранять состояние между вызовами HTTP-сервисов с помощью управления сеансами. Это позволяет использовать стандартное временное хранилище ПоместитьВоВременноеХранилище, не опасаясь, что данные исчезнут из-за смены сеанса.

Рассмотрим подробнее алгоритм настройки и работы этого механизма:

  1. Настройка публикации: В свойствах HTTP-сервиса в конфигураторе (или непосредственно в файле публикации default.vrd) необходимо включить повторное использование сеансов. Для этого параметру ReuseSessions (Повторное использование сеансов) задается значение Use (Использовать).
  2. Определение таймаута: Параметр SessionMaxAge (Время жизни сеанса) задает время бездействия сеанса в секундах. Например, можно установить 300 секунд (5 минут), чего с запасом хватит на передачу даже очень тяжелых чанков.
  3. Инициализация сессии клиентом: Первый запрос клиент отправляет со специальным заголовком IBSession: start. Платформа 1С выделяет под этот запрос персональный сеанс и возвращает клиенту заголовок Set-Cookie: ibsession=ИдентификаторСеанса.
  4. Передача порций данных: Все последующие HTTP-запросы с частями файлов клиент отправляет с заголовком Cookie: ibsession=ИдентификаторСеанса. Платформа гарантирует, что все эти запросы будут выполнены в рамках одного и того же сеанса 1С. Теперь данные в ПоместитьВоВременноеХранилище будут накапливаться без потерь.
  5. Завершение сессии: Последний запрос (после успешной сборки и обработки файла) отправляется с заголовком IBSession: finish. Это сообщает платформе, что сеанс можно закрыть, высвобождая оперативную память сервера.

Этот способ является наиболее красивым и «чистым» с точки зрения кода 1С, так как не требует создания дополнительных таблиц в базе данных или временных файлов на диске.

Решение 2. Использование служебного регистра сведений

Если изменять параметры публикации веб-сервера или управлять HTTP-заголовками на клиенте нет возможности, классическим и максимально надежным решением является использование служебного регистра сведений. Этот способ был активно поддержан участниками обсуждения на форуме.

Разберем по шагам, как спроектировать такую систему:

  1. Создаем неподчиненный регистр сведений (например, БуферЗагрузкиФайлов) с режимом записи «Запись независимая».
  2. Определяем структуру измерений:
    • ИдентификаторПакета (Тип: Строка или УникальныйИдентификатор) — связывает все части одного файла.
    • НомерЧасти (Тип: Число) — определяет порядок сборки файла.
  3. Определяем ресурсы:
    • ДанныеЧасти (Тип: ХранилищеЗначения) — содержит бинарные данные конкретного чанка.
    • ДатаЗаписи (Тип: Дата) — необходима для последующей очистки зависших данных по таймауту.

При получении очередного HTTP-запроса с частью файла мы записываем её в регистр сведений:


&НаСервере
Процедура ЗаписатьЧастьФайлаВРегистр(ИдентификаторПакета, НомерЧасти, ДвоичныеДанныеЧасти)
    
    МенеджерЗаписи = РегистрыСведений.БуферЗагрузкиФайлов.СоздатьМенеджерЗаписи();
    МенеджерЗаписи.ИдентификаторПакета = ИдентификаторПакета;
    МенеджерЗаписи.НомерЧасти          = НомерЧасти;
    МенеджерЗаписи.ДанныеЧасти         = Новый ХранилищеЗначения(ДвоичныеДанныеЧасти);
    МенеджерЗаписи.ДатаЗаписи          = ТекущаяДата();
    
    МенеджерЗаписи.Записать(Истина);
    
КонецПроцедуры

После того как клиент передал все части (информацию о количестве частей можно передавать в заголовках или в первом/последнем запросе), мы считываем все записи регистра по нашему ИдентификаторПакета, сортируем их по НомерЧасти, последовательно склеиваем двоичные данные и отправляем файл на обработку. После завершения обработки обязательно очищаем регистр для этого пакета, чтобы не раздувать базу данных.

Преимущества: Полная отказоустойчивость. Даже если сервер 1С перезапустится посреди ночи во время передачи файла, ранее загруженные части никуда не исчезнут, и клиент сможет продолжить загрузку с прерванного места.

Недостатки: Высокая нагрузка на СУБД из-за частой записи тяжелых бинарных данных, а также рост транзакционного лога.

Решение 3. Использование временных файлов на диске сервера

Еще один классический вариант — сваливать части во временные файлы на сервере и затем «склеивать» их. Это позволяет избежать нагрузки на СУБД, перенося работу на файловую систему.

Проанализируем алгоритм реализации:

  1. При первом запросе генерируем уникальный GUID пакета и создаем на сервере временную директорию (используя КаталогВременныхФайлов()).
  2. Каждую полученную часть сохраняем в эту директорию под именем, соответствующим её порядковому номеру (например, part_1.tmp, part_2.tmp).
  3. При получении финального запроса выполняем склейку. Для этого в платформе 1С очень удобно использовать потоки чтения и записи данных.

Рассмотрим пример кода сборки итогового файла из частей на диске:


&НаСервере
Функция СклеитьФайлИзЧастей(КаталогЧастей, КоличествоЧастей) Экспорт
    
    ИмяИтоговогоФайла = ПолучитьИмяВременногоФайла("tmp");
    ЗаписьРезультата = Новый ЗаписьДанных(ИмяИтоговогоФайла);
    
    Для Индекс = 1 По КоличествоЧастей Цикл
        ИмяФайлаЧасти = КаталогЧастей + "part_" + Строка(Индекс) + ".tmp";
        ФайлЧасти = Новый Файл(ИмяФайлаЧасти);
        
        Если ФайлЧасти.Существует() Тогда
            ЧтениеЧасти = Новый ЧтениеДанных(ИмяФайлаЧасти);
            ЧтениеЧасти.КопироватьВ(ЗаписьРезультата);
            ЧтениеЧасти.Закрыть();
            
            // Удаляем временный файл части сразу после копирования
            УдалитьФайлы(ИмяФайлаЧасти);
        Иначе
            ВызватьИсключение "Отсутствует часть файла: " + ИмяФайлаЧасти;
        КонецЕсли;
    КонецЦикла;
    
    ЗаписьРезультата.Закрыть();
    Возврат ИмяИтоговогоФайла;
    
КонецФункции

Использование объектов ЗаписьДанных и ЧтениеДанных позволяет склеивать файлы потоково, не загружая их целиком в оперативную память сервера 1С, что гарантирует стабильность работы процесса rphost даже при обработке многогигабайтных файлов.

Решение 4. Использование готовой подсистемы БСП «Передача данных»

Если вы работаете в конфигурации, созданной на базе Библиотеки стандартных подсистем (БСП), вам не нужно изобретать велосипед. В БСП есть готовый служебный HTTP-сервис «ПередачаДанных».

Этот сервис спроектирован специально для надежной трансляции файлов огромных размеров (до 20 ГБ и более) между информационными базами или внешними приложениями. Подсистема автоматически решает все сопутствующие проблемы:

Обязательно изучите документацию к вашей версии БСП, возможно, данный функционал уже доступен в вашей системе «из коробки».

Решение 5. Архитектурный паттерн: Внешнее объектное хранилище (S3) + метаданные

При работе под высокими нагрузками передача тяжелых бинарных данных напрямую через веб-сервер 1С является плохой практикой (антипаттерном). При обработке входящего потока платформа создает временные файлы, парсит multipart-запросы, загружает память рабочего процесса, что негативно сказывается на работе обычных пользователей 1С.

Современный архитектурный подход заключается в делегировании этой задачи специализированным сервисам:

  1. Клиент загружает тяжелый файл по частям не в 1С, а напрямую в S3-совместимое облачное хранилище (например, MinIO, Yandex Cloud Object Storage или Amazon S3). Эти решения оптимизированы для приема терабайтных объемов данных на максимальной скорости сети.
  2. После успешной загрузки файла в S3 клиент отправляет на HTTP-сервис 1С легкий JSON-запрос, содержащий только метаданные: имя файла, размер, MD5-хэш и временную ссылку (Presigned URL) на скачивание этого файла из хранилища.
  3. 1С принимает этот JSON мгновенно, регистрирует задачу в очереди (например, через регламентное задание) и сразу возвращает клиенту ответ 200 OK.
  4. В фоновом режиме рабочий процесс 1С скачивает файл из S3 (потоково, обрабатывая данные порциями) и выполняет необходимую бизнес-логику, не нагружая веб-сервер и сеансы пользователей.

Решение 6. Использование быстрого кэша в оперативной памяти (Redis)

Если данные, которые передает клиент, представляют собой не монолитный файл, а огромный массив структурированных данных (например, миллионы строк JSON), которые нужно именно накопить и сравнить с базой перед загрузкой, идеальным решением будет использование внешнего NoSQL-хранилища типа Redis или Memcached.

При получении порций данных HTTP-сервис 1С с помощью внешней компоненты или через REST API отправляет их в Redis по ключу, соответствующему уникальному идентификатору сессии обмена. Для этого ключа обязательно устанавливается TTL (Time To Live, например, 15-30 минут).

После того как все порции данных собраны в Redis, 1С запускает фоновое задание, которое быстро считывает накопленный массив непосредственно из оперативной памяти Redis, сопоставляет его с СУБД 1С и производит загрузку. Это решает проблему скорости работы (чтение и запись в оперативную память происходят мгновенно) и полностью исключает блокировки СУБД 1С при накоплении пакетов.

Решение 7. Тонкая настройка веб-серверов (IIS / Apache)

Независимо от выбранного способа хранения данных в 1С, при передаче больших объемов вы можете столкнуться с тем, что веб-сервер прерывает соединение еще до того, как запрос дойдет до платформы 1С. Обычно возвращается ошибка 413 Request Entity Too Large.

Выясним причину и настроим лимиты:

Резюме: что выбрать?

Мы проанализировали различные варианты решения проблемы хранения объемных данных при работе с HTTP-сервисами в 1С. Подведем итог:

← На главную