Передача больших объемов данных через HTTP-сервисы в 1С — классическая задача интеграции. Часто возникает ситуация, когда внешняя система должна передать тяжелый файл (например, выгрузку товаров, логов или документов), размер которого измеряется сотнями мегабайт или даже гигабайтами. Прямая передача такого объема в рамках одного HTTP-запроса неминуемо приводит к проблемам: исчерпанию оперативной памяти на сервере, таймаутам веб-сервера (IIS/Apache) или аварийному завершению рабочего процесса rphost.
Единственным надежным выходом является разбиение данных на части (чанги) и их последовательная передача. Однако тут разработчики сталкиваются с ключевой проблемой: где накапливать эти части на стороне 1С при последовательных запросах, если стандартное временное хранилище очищается уже через 20 секунд простоя?
В этой статье мы подробно разберем причины такого поведения платформы 1С, рассмотрим классические обходные пути, предложенные разработчиками на форумах, а также проанализируем современные штатные механизмы и архитектурные паттерны, которые позволяют решить эту задачу максимально эффективно и быстро.
Проанализируем ситуацию, с которой столкнулся автор темы. При попытке использовать метод ПоместитьВоВременноеХранилище данные бесследно исчезают, если между запросами возникает пауза более 20 секунд. Участники форума предположили, что дело в перезапуске сеансов, и они абсолютно правы.
По умолчанию HTTP-сервисы в платформе 1С работают в режиме stateless (без сохранения состояния). Платформа оптимизирует использование ресурсов сервера с помощью автоматического пула сеансов. Это означает следующее:
e1cib/tempstorage/...) теряется.Чтобы обойти это ограничение, нам нужны механизмы, которые либо свяжут запросы с одним и тем же сеансом, либо позволят хранить состояние (данные) вне сеанса 1С. Рассмотрим все возможные решения по шагам.
Начиная с версии платформы 1С 8.3.9, разработчики добавили штатную возможность сохранять состояние между вызовами HTTP-сервисов с помощью управления сеансами. Это позволяет использовать стандартное временное хранилище ПоместитьВоВременноеХранилище, не опасаясь, что данные исчезнут из-за смены сеанса.
Рассмотрим подробнее алгоритм настройки и работы этого механизма:
default.vrd) необходимо включить повторное использование сеансов. Для этого параметру ReuseSessions (Повторное использование сеансов) задается значение Use (Использовать).SessionMaxAge (Время жизни сеанса) задает время бездействия сеанса в секундах. Например, можно установить 300 секунд (5 минут), чего с запасом хватит на передачу даже очень тяжелых чанков.IBSession: start. Платформа 1С выделяет под этот запрос персональный сеанс и возвращает клиенту заголовок Set-Cookie: ibsession=ИдентификаторСеанса.Cookie: ibsession=ИдентификаторСеанса. Платформа гарантирует, что все эти запросы будут выполнены в рамках одного и того же сеанса 1С. Теперь данные в ПоместитьВоВременноеХранилище будут накапливаться без потерь.IBSession: finish. Это сообщает платформе, что сеанс можно закрыть, высвобождая оперативную память сервера.Этот способ является наиболее красивым и «чистым» с точки зрения кода 1С, так как не требует создания дополнительных таблиц в базе данных или временных файлов на диске.
Если изменять параметры публикации веб-сервера или управлять HTTP-заголовками на клиенте нет возможности, классическим и максимально надежным решением является использование служебного регистра сведений. Этот способ был активно поддержан участниками обсуждения на форуме.
Разберем по шагам, как спроектировать такую систему:
БуферЗагрузкиФайлов) с режимом записи «Запись независимая».ИдентификаторПакета (Тип: Строка или УникальныйИдентификатор) — связывает все части одного файла.НомерЧасти (Тип: Число) — определяет порядок сборки файла.ДанныеЧасти (Тип: ХранилищеЗначения) — содержит бинарные данные конкретного чанка.ДатаЗаписи (Тип: Дата) — необходима для последующей очистки зависших данных по таймауту.При получении очередного HTTP-запроса с частью файла мы записываем её в регистр сведений:
&НаСервере
Процедура ЗаписатьЧастьФайлаВРегистр(ИдентификаторПакета, НомерЧасти, ДвоичныеДанныеЧасти)
МенеджерЗаписи = РегистрыСведений.БуферЗагрузкиФайлов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.ИдентификаторПакета = ИдентификаторПакета;
МенеджерЗаписи.НомерЧасти = НомерЧасти;
МенеджерЗаписи.ДанныеЧасти = Новый ХранилищеЗначения(ДвоичныеДанныеЧасти);
МенеджерЗаписи.ДатаЗаписи = ТекущаяДата();
МенеджерЗаписи.Записать(Истина);
КонецПроцедуры
После того как клиент передал все части (информацию о количестве частей можно передавать в заголовках или в первом/последнем запросе), мы считываем все записи регистра по нашему ИдентификаторПакета, сортируем их по НомерЧасти, последовательно склеиваем двоичные данные и отправляем файл на обработку. После завершения обработки обязательно очищаем регистр для этого пакета, чтобы не раздувать базу данных.
Преимущества: Полная отказоустойчивость. Даже если сервер 1С перезапустится посреди ночи во время передачи файла, ранее загруженные части никуда не исчезнут, и клиент сможет продолжить загрузку с прерванного места.
Недостатки: Высокая нагрузка на СУБД из-за частой записи тяжелых бинарных данных, а также рост транзакционного лога.
Еще один классический вариант — сваливать части во временные файлы на сервере и затем «склеивать» их. Это позволяет избежать нагрузки на СУБД, перенося работу на файловую систему.
Проанализируем алгоритм реализации:
КаталогВременныхФайлов()).part_1.tmp, part_2.tmp).Рассмотрим пример кода сборки итогового файла из частей на диске:
&НаСервере
Функция СклеитьФайлИзЧастей(КаталогЧастей, КоличествоЧастей) Экспорт
ИмяИтоговогоФайла = ПолучитьИмяВременногоФайла("tmp");
ЗаписьРезультата = Новый ЗаписьДанных(ИмяИтоговогоФайла);
Для Индекс = 1 По КоличествоЧастей Цикл
ИмяФайлаЧасти = КаталогЧастей + "part_" + Строка(Индекс) + ".tmp";
ФайлЧасти = Новый Файл(ИмяФайлаЧасти);
Если ФайлЧасти.Существует() Тогда
ЧтениеЧасти = Новый ЧтениеДанных(ИмяФайлаЧасти);
ЧтениеЧасти.КопироватьВ(ЗаписьРезультата);
ЧтениеЧасти.Закрыть();
// Удаляем временный файл части сразу после копирования
УдалитьФайлы(ИмяФайлаЧасти);
Иначе
ВызватьИсключение "Отсутствует часть файла: " + ИмяФайлаЧасти;
КонецЕсли;
КонецЦикла;
ЗаписьРезультата.Закрыть();
Возврат ИмяИтоговогоФайла;
КонецФункции
Использование объектов ЗаписьДанных и ЧтениеДанных позволяет склеивать файлы потоково, не загружая их целиком в оперативную память сервера 1С, что гарантирует стабильность работы процесса rphost даже при обработке многогигабайтных файлов.
Если вы работаете в конфигурации, созданной на базе Библиотеки стандартных подсистем (БСП), вам не нужно изобретать велосипед. В БСП есть готовый служебный HTTP-сервис «ПередачаДанных».
Этот сервис спроектирован специально для надежной трансляции файлов огромных размеров (до 20 ГБ и более) между информационными базами или внешними приложениями. Подсистема автоматически решает все сопутствующие проблемы:
Обязательно изучите документацию к вашей версии БСП, возможно, данный функционал уже доступен в вашей системе «из коробки».
При работе под высокими нагрузками передача тяжелых бинарных данных напрямую через веб-сервер 1С является плохой практикой (антипаттерном). При обработке входящего потока платформа создает временные файлы, парсит multipart-запросы, загружает память рабочего процесса, что негативно сказывается на работе обычных пользователей 1С.
Современный архитектурный подход заключается в делегировании этой задачи специализированным сервисам:
200 OK.Если данные, которые передает клиент, представляют собой не монолитный файл, а огромный массив структурированных данных (например, миллионы строк JSON), которые нужно именно накопить и сравнить с базой перед загрузкой, идеальным решением будет использование внешнего NoSQL-хранилища типа Redis или Memcached.
При получении порций данных HTTP-сервис 1С с помощью внешней компоненты или через REST API отправляет их в Redis по ключу, соответствующему уникальному идентификатору сессии обмена. Для этого ключа обязательно устанавливается TTL (Time To Live, например, 15-30 минут).
После того как все порции данных собраны в Redis, 1С запускает фоновое задание, которое быстро считывает накопленный массив непосредственно из оперативной памяти Redis, сопоставляет его с СУБД 1С и производит загрузку. Это решает проблему скорости работы (чтение и запись в оперативную память происходят мгновенно) и полностью исключает блокировки СУБД 1С при накоплении пакетов.
Независимо от выбранного способа хранения данных в 1С, при передаче больших объемов вы можете столкнуться с тем, что веб-сервер прерывает соединение еще до того, как запрос дойдет до платформы 1С. Обычно возвращается ошибка 413 Request Entity Too Large.
Выясним причину и настроим лимиты:
system.webServer/security/requestFiltering и увеличьте значение параметра maxAllowedContentLength (задает максимальный размер запроса в байтах).uploadReadAheadSize в разделе system.webServer/serverRuntime.httpd.conf (или файл конфигурации вашего виртуального хоста).LimitRequestBody.LimitRequestBody 104857600 для лимита в 100 МБ). Значение 0 убирает ограничение полностью.Мы проанализировали различные варианты решения проблемы хранения объемных данных при работе с HTTP-сервисами в 1С. Подведем итог: