Передача данных между распределенными базами 1С — одна из самых частых задач интеграции. Когда речь заходит о справочнике Номенклатура, мы часто сталкиваемся с огромными объемами данных, которые могут исчисляться десятками и сотнями тысяч позиций. В такой ситуации стандартные подходы «выгрузить всё в одну строку» перестают работать из-за нехватки оперативной памяти или тайм-аутов веб-сервера. Рассмотрим подробнее, как правильно выстроить процесс передачи данных, чтобы система работала стабильно и быстро.
Разберем типичную ситуацию: программист пытается выбрать всю номенклатуру одним запросом и упаковать её в JSON. Основная ошибка здесь заключается в попытке обработать весь массив данных целиком в оперативной памяти. Проанализируем, как можно улучшить этот процесс на этапе выборки данных.
Прежде всего, нам необходимо отказаться от получения всех полей через объектную модель ПолучитьОбъект() внутри цикла, так как это крайне медленно. Выясним причину: каждый вызов ПолучитьОбъект() инициирует отдельное обращение к базе данных. Вместо этого лучше сразу выбирать необходимые поля в запросе или использовать механизм СериализаторXDTO для ссылки, если нам нужна полная копия данных.
Рассмотрим правильный подход к формированию запроса в передающей базе:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Артикул КАК Артикул,
| Номенклатура.ЕдиницаИзмерения.Наименование КАК ЕдиницаНаименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа";
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Важный совет: Чтобы передавать только измененные данные, рекомендуется использовать планы обмена или проверку версии данных. Это позволит не гонять по сети всю базу каждый раз, а передавать только «дельту».
Посмотрим на пример формирования ответа в HTTP-сервисе. Вместо того чтобы просто возвращать строку, мы должны явно указать тип контента и корректно передать тело сообщения. Разберем код, который должен располагаться в методе GET или POST вашего HTTP-сервиса:
Функция ПолучитьДанные(Запрос)
// Формируем JSON
ЗаписьJSON = Новый ЗаписьJSON();
ЗаписьJSON.УстановитьСтроку();
// Сериализуем данные...
СтрJSON = ЗаписьJSON.Закрыть();
// Формируем корректный HTTP-ответ
Ответ = Новый HTTPСервисОтвет(200);
Ответ.Заголовки.Вставить("Content-Type", "application/json;charset=utf-8");
Ответ.УстановитьТелоИзСтроки(СтрJSON);
Возврат Ответ;
КонецФункции
Использование Content-Type: application/json необходимо для того, чтобы принимающая сторона (база-приемник) сразу понимала, в каком формате пришли данные, и могла автоматически их распарсить.
Если справочник номенклатуры действительно большой, использование УстановитьТелоИзСтроки() приведет к ошибке «Недостаточно памяти». Проанализируем ситуацию: строка в 1С занимает много места, и попытка выделить под неё непрерывный блок памяти в несколько сотен мегабайт часто заканчивается крахом процесса.
Выходом является потоковая запись в файл. Разберем по шагам, как это реализовать:
ПолучитьИмяВременногоФайла().ЗаписьJSON с привязкой к этому файлу.УстановитьТелоИзФайла() у объекта HTTPСервисОтвет.Этот подход позволяет 1С не загружать весь объем данных в память, а передавать файл «кусками» непосредственно через веб-сервер. Посмотрим на реализацию:
ИмяФайла = ПолучитьИмяВременногоФайла("json");
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.ОткрытьФайл(ИмяФайла);
ЗаписьJSON.ЗаписатьНачалоМассива();
Пока Выборка.Следующий() Цикл
// Записываем данные каждого элемента
СериализаторXDTO.ЗаписатьJSON(ЗаписьJSON, Выборка.Ссылка.ПолучитьОбъект());
КонецЦикла;
ЗаписьJSON.ЗаписатьКонецМассива();
ЗаписьJSON.Закрыть();
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзФайла(ИмяФайла);
Возврат Ответ;
Даже при использовании потоков, один HTTP-запрос может длиться слишком долго, что приведет к разрыву соединения по тайм-ауту (обычно это 60 секунд на уровне IIS или Apache). Выясним, как этого избежать. Лучшим решением будет пагинация — разделение выдачи на страницы (порции).
Рассмотрим алгоритм:
skip (сколько пропустить) и take (сколько записей взять).ПЕРВЫЕ и сортировкой по Ссылка.Пример запроса с пагинацией на стороне источника:
"ВЫБРАТЬ ПЕРВЫЕ 1000
| Номенклатура.Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка > &ПоследняяСсылка
|УПОРЯДОЧИТЬ ПО
| Ссылка"
Часто новички путают предназначение заголовка multipart/form-data. Важно понимать: этот формат сам по себе не «разбивает» данные на части для стабильной передачи. Он лишь позволяет отправить в одном запросе разные типы данных (например, JSON-описание товара и его изображение в бинарном виде). Если вы передаете только текст (JSON), использование multipart лишь усложнит вам жизнь, так как потребует ручного парсинга границ (boundary) на стороне приемника.
Что касается «запихивания в двоичные данные», о котором упоминалось на форуме: это имеет смысл только в одном случае — если вы хотите предварительно сжать данные в ZIP-архив. Это действительно ускорит передачу, так как текстовый JSON сжимается в 5-10 раз. В этом случае вы записываете JSON в файл, архивируете его, и передаете архив как ДвоичныеДанные.
Выясним, как правильно прочитать полученные данные. На стороне приемника мы используем HTTPСоединение и объект ЧтениеJSON. Чтобы не перегружать память приемника, мы также должны использовать чтение из файла:
Ответ = Соединение.ВызватьHTTPМетод("GET", Запрос);
ИмяФайла = ПолучитьИмяВременногоФайла();
Ответ.ЗаписатьТелоВФайл(ИмяФайла);
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.ОткрытьФайл(ИмяФайла);
Данные = ПрочитатьJSON(ЧтениеJSON); // Если данных очень много, читаем в цикле через ЧтениеJSON.Прочитать()
ЧтениеJSON.Закрыть();
Таким образом, мы построили надежную цепочку передачи данных, которая не зависит от размера справочника и стабильно работает даже на слабых каналах связи. Сочетание пагинации и потоковой записи в файлы — это «золотой стандарт» для интеграций на платформе 1С:Предприятие 8.3.