При интеграции 1С с внешними системами разработчики часто сталкиваются с ситуацией, когда API стороннего сервиса возвращает огромный массив данных в формате JSON, но не поддерживает пагинацию. Если попытаться получить такой ответ стандартным методом Ответ.ПолучитьТелоКакСтроку(), система может аварийно завершиться с ошибкой "Недостаточно памяти", так как строка в несколько сотен миллионов символов требует колоссального объема RAM. Разберем, как правильно организовать потоковую обработку данных, чтобы минимизировать нагрузку на сервер или клиент.
Проанализируем ситуацию: когда мы вызываем метод Получить(), платформа 1С по умолчанию пытается загрузить все тело ответа в оперативную память. Если объем данных составляет, например, 200 МБ, то в кодировке Unicode в памяти 1С эта строка может занять 400 МБ. Однако на этом расходы не заканчиваются. При парсинге этого текста в объекты Структура или Соответствие через функцию ПрочитатьJSON() потребление памяти возрастает в 5-10 раз из-за создания множества объектов платформы. Для 32-битного клиента (предел около 2 ГБ) это почти всегда фатально.
Первый и самый простой способ избежать переполнения памяти при получении данных — это запись ответа напрямую на диск, минуя оперативную память. Рассмотрим, как это реализовать с помощью метода Получить() объекта HTTPСоединение.
В этом случае данные «стримятся» сразу в файл. Посмотрим на пример кода:
// Подготавливаем соединение и запрос
Соединение = Новый HTTPСоединение("api.service.com",,,, Неопределено, 60);
Запрос = Новый HTTPЗапрос("/v1/get_huge_data");
// Генерируем имя временного файла, куда будет записан ответ
ИмяФайла = ПолучитьИмяВременногоФайла("json");
// Ключевой момент: указываем имя файла вторым параметром
Ответ = Соединение.Получить(Запрос, ИмяФайла);
Если Ответ.КодСостояния = 200 Тогда
// Теперь данные на диске, а не в памяти.
// Важно: НЕ используйте ТекстовыйДокумент.Прочитать() или Ответ.ПолучитьТелоКакСтроку()!
// Читайте файл только через специализированные потоковые инструменты.
ОбработатьФайлJSON(ИмяФайла);
КонецЕсли;
УдалитьФайлы(ИмяФайла);
Важное предостережение: Ошибка многих разработчиков заключается в том, что после сохранения данных в файл они пытаются прочитать его целиком методом Т.ПолучитьТекст(). Это полностью аннулирует пользу от записи в файл, так как огромная строка снова оказывается в памяти. Вместо этого нужно использовать последовательное чтение.
Начиная с современных версий платформы 8.3, 1С позволяет работать с телом HTTP-ответа как с потоком. Это наиболее элегантный и производительный способ, так как он не требует создания временных файлов на диске (в некоторых случаях) и позволяет начать парсинг еще до того, как весь ответ будет полностью загружен из сети.
Рассмотрим пример использования метода ПолучитьТелоКакПоток():
Ответ = Соединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда
Чтение = Новый ЧтениеJSON;
// Открываем поток чтения напрямую из тела HTTP-ответа
Чтение.ОткрытьПоток(Ответ.ПолучитьТелоКакПоток());
// Вариант А: Быстрое чтение, если памяти достаточно
// ДанныеОтвета = ПрочитатьJSON(Чтение, Истина);
// Вариант Б: Истинное потоковое (событийное) чтение
Пока Чтение.Прочитать() Цикл
ТипУзла = Чтение.ТипТекущегоУзла;
Если ТипУзла = ТипУзлаЧтенияJSON.ИмяСвойства И Чтение.ТекущееЗначение = "Items" Тогда
// Нашли начало массива элементов, обрабатываем их по одному
Пока Чтение.Прочитать() Цикл
Если Чтение.ТипТекущегоУзла = ТипУзлаЧтенияJSON.КонецМассива Тогда
Прервать;
КонецЕсли;
// Читаем один объект из массива, не загружая остальные
ОбъектДанных = ПрочитатьJSON(Чтение);
ОбработатьОбъект(ОбъектДанных);
КонецЦикла;
КонецЕсли;
КонецЦикла;
Чтение.Закрыть();
КонецЕсли;
Выясним, почему стандартная функция ПрочитатьJSON(ЧтениеJSON) может не подойти. Она строит в памяти полное дерево объектов (Соответствия и Массивы). Если JSON содержит 1 миллион записей, 1С создаст 1 миллион объектов Соответствие, что приведет к краху.
Чтобы этого избежать, мы используем цикл Пока ЧтениеJSON.Прочитать() Цикл. В этом режиме мы «бежим» по узлам JSON:
Таким образом, в памяти одновременно находится только одна небольшая структура, независимо от того, весит ли исходный JSON 100 МБ или 10 ГБ.
Проанализируем еще несколько технических нюансов, которые помогут ускорить работу с большими данными:
Accept-Encoding: gzip. Большинство современных веб-серверов сожмут JSON-текст перед отправкой, что уменьшит объем передаваемого трафика в 5-10 раз. Платформа 1С автоматически распакует такой поток при чтении.HTTPСоединение (параметр Таймаут), установив значение 120-300 секунд вместо стандартных 30.?date_from=...&date_to=... или ?limit=1000&offset=0). Работа с частями данных всегда надежнее, чем чтение гигантских потоков.Подведем итог: для работы с большими ответами всегда используйте HTTPСоединение.Получить(...) с записью в файл или ПолучитьТелоКакПоток(). Никогда не превращайте тело ответа в одну гигантскую строку и используйте последовательное чтение JSON через цикл, чтобы сохранить стабильность вашей системы.