Как оптимизировать HTTP-сервис 1С для передачи больших объемов данных

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

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

Анализ проблемы производительности в циклах

Рассмотрим типичную ошибку, которую часто совершают при формировании массива структур для последующей сериализации в JSON. Проанализируем ситуацию, когда данные сначала выбираются в ТаблицаЗначений, а затем обходятся в цикле для создания массива структур. Изначально код может выглядеть крайне неоптимально, как в примере ниже:


Для Каждого СтрокаТЗ Из ТЗ Цикл
    СтруктураСтроки = Новый Структура;
    КолонкаИндекс = 0;
    Для Каждого Колонка Из ТЗ.Колонки Цикл
        ИмяКолонки = Колонка.Имя;
        Значение = Строка(СтрокаТЗ[КолонкаИндекс]);
        КолонкаИндекс = КолонкаИндекс + 1;
        Попытка
            СтруктураСтроки.Вставить(ИмяКолонки, Значение);
        Исключение
            // Медленная операция
            Выполнить("СтруктураСтроки.Вставить(""" + ИмяКолонки + """,)");
            СтруктураСтроки[ИмяКолонки] = Значение;
        КонецПопытки;
    КонецЦикла;
    МассивРезультат.Добавить(СтруктураСтроки);
КонецЦикла;

Выясним причину медленной работы этого фрагмента. Во-первых, использование конструкции Попытка...Исключение внутри вложенного цикла для 80 000 строк создает колоссальные накладные расходы. Во-вторых, динамическое создание имен колонок и использование Выполнить() — это крайне ресурсозатратные операции. Проанализируем, как можно оптимизировать этот процесс.

Оптимизация формирования структур данных

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

  1. Сформируем строку с именами колонок один раз перед началом основного цикла.
  2. Используем конструктор Новый Структура(СписокПолей), который работает значительно быстрее, чем последовательные вызовы Вставить().
  3. Применим платформенный метод ЗаполнитьЗначенияСвойств(), который эффективно копирует данные из строки таблицы значений в структуру.

Посмотрим на оптимизированный пример кода:


// 1. Формируем список имен колонок заранее
СписокИменКолонок = "";
Для Каждого Колонка Из ТЗ.Колонки Цикл
    СписокИменКолонок = СписокИменКолонок + ?(СписокИменКолонок = "", "", ",") + Колонка.Имя;
КонецЦикла;

// 2. Основной цикл без лишних проверок
Для Каждого СтрокаТЗ Из ТЗ Цикл
    // Создаем структуру сразу со всеми полями
    СтруктураСтроки = Новый Структура(СписокИменКолонок);
    // Копируем данные одним вызовом
    ЗаполнитьЗначенияСвойств(СтруктураСтроки, СтрокаТЗ);
    МассивРезультат.Добавить(СтруктураСтроки);
КонецЦикла;

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

Использование методов БСП для конвертации данных

Если в вашей конфигурации используется Библиотека Стандартных Подсистем (БСП), мы рекомендуем использовать готовые и оптимизированные функции. Например, в модуле ОбщегоНазначения существует метод ТаблицаЗначенийВМассив(). Проанализируем его использование:


// Вместо ручного обхода используем стандартную функцию
МассивРезультат = ОбщегоНазначения.ТаблицаЗначенийВМассив(ТЗ);

Этот метод написан с учетом максимальной производительности и избавляет разработчика от необходимости писать собственные циклы трансформации данных. Однако стоит помнить, что при очень больших объемах данных (сотни тысяч строк) создание огромного массива в памяти может привести к ошибке нехватки памяти (Out of Memory).

Реализация пагинации (порционной загрузки)

Когда данных слишком много для одного HTTP-ответа, правильной практикой считается использование пагинации. Это позволяет принимающей системе запрашивать данные частями. Рассмотрим, как реализовать параметры top (количество записей) и skip (сколько пропустить), аналогично протоколу OData.

Пример формирования URL для запроса порции данных: https://server/api/v1/docs?top=1000&skip=2000. Разберем, как обработать эти параметры в коде HTTP-сервиса:


Функция ПолучитьДанные(Запрос)
    
    ПараметрыЗапроса = Запрос.ПараметрыЗапроса;
    КоличествоЗаписей = ПараметрыЗапроса.Получить("top");
    ПропуститьЗаписей = ПараметрыЗапроса.Получить("skip");
    
    // Преобразование параметров в число
    ЧислоTop = ?(КоличествоЗаписей <> Неопределено, Число(КоличествоЗаписей), 1000);
    ЧислоSkip = ?(ПропуститьЗаписей <> Неопределено, Число(ПропуститьЗаписей), 0);
    
    // В запросе к базе данных используем ВЫБРАТЬ ПЕРВЫЕ (ЧислоTop + ЧислоSkip)
    // А при обходе результата просто пропускаем первые ЧислоSkip итераций
    
    // ... логика получения данных ...
    
КонецФункции

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

Потоковая запись JSON: работаем с памятью эффективно

Одной из самых ресурсоемких операций является сериализация всего массива структур в одну длинную строку JSON. При объеме в 80 000 строк такая строка может занимать сотни мегабайт. Чтобы избежать проблем с памятью, рассмотрим использование объекта ЗаписьJSON.

Вместо формирования промежуточного массива структур, мы можем записывать данные напрямую в поток. Проанализируем этот метод:


ЗаписьJSON = Новый ЗаписьJSON;
// Записываем в строку или сразу в файл
ЗаписьJSON.УстановитьСтроку(); 

ЗаписьJSON.ЗаписатьНачалоМассива();

Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
    ЗаписьJSON.ЗаписатьНачалоОбъекта();
    
    Для Каждого Колонка Из РезультатЗапроса.Колонки Цикл
        ЗаписьJSON.ЗаписатьИмяСвойства(Колонка.Имя);
        ЗаписьJSON.ЗаписатьЗначение(Выборка[Колонка.Имя]);
    КонецЦикла;
    
    ЗаписьJSON.ЗаписатьКонецОбъекта();
КонецЦикла;

ЗаписьJSON.ЗаписатьКонецМассива();

РезультатСтрокой = ЗаписьJSON.Закрыть();

Используя ЗаписьJSON, мы не создаем тысячи объектов Структура в памяти. Платформа сразу формирует текстовое представление данных, что существенно экономит ресурсы сервера.

Дополнительные методы ускорения

Рассмотрим еще несколько подходов, которые помогут сделать ваш HTTP-сервис еще быстрее:

Подведем итог. Для оптимизации HTTP-сервиса с большими данными необходимо: во-первых, отказаться от медленных циклов и ручного обхода колонок в пользу ЗаполнитьЗначенияСвойств; во-вторых, внедрить пагинацию для разделения данных на части; и в-третьих, использовать потоковую запись JSON для экономии оперативной памяти. Комбинация этих методов позволит вашему сервису стабильно работать даже с очень большими объемами информации.

← На главную