Можно ли запустить регламентное задание 1С не в фоновом режиме и как ускорить обработку очереди данных?

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

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

Архитектура регламентных и фоновых заданий в 1С:Предприятие

Рассмотрим, как платформа 1С выполняет запланированные процедуры на уровне сервера приложений. В клиент-серверном варианте информационной базы существует строгое разделение понятий:

  1. Регламентное задание (РегламентноеЗадание) — это объект метаданных и расписание в базе данных. Само по себе оно не выполняет код, а лишь указывает платформе, какую процедуру общего модуля и с какой периодичностью необходимо вызвать.
  2. Фоновое задание (ФоновоеЗадание) — это непосредственный исполняемый процесс на стороне кластера серверов 1С. Когда наступает время по расписанию, менеджер кластера запускает процедуру регламентного задания исключительно в виде отдельного фонового задания внутри рабочего процесса rphost.

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

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

  1. Код выполняется непосредственно на сервере рядом с СУБД, без сетевых задержек на передачу данных клиенту.
  2. Полностью отсутствуют накладные расходы на визуализацию форм, отрисовку элементов интерфейса и обработку пользовательских событий.
  3. Серверные процессы оптимизированы под пакетную работу с большими объемами данных.

Если фоновая обработка одного заказа занимает 4 минуты, причина кроется не в способе запуска, а в структуре алгоритма, блокировках данных или неоптимальной архитектуре чтения.

Архитектурное решение 1: Организация буферной очереди через регистр сведений

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

Создадим регистр сведений ОчередьВходящихПакетов со следующими реквизитами:

  1. ИдентификаторПакета (УникальныйИдентификатор / Строка) — измерение.
  2. ДанныеФайла (ХранилищеЗначения) — ресурс (бинарные данные заказа).
  3. Статус (ПеречислениеСсылка.СтатусыОбработки: Новый, ВРаботе, Ошибка, Обработан) — реквизит.
  4. ДатаСоздания (Дата) — реквизит.

Посмотрим на код регламентного задания-диспетчера, который за доли секунды считывает файлы из каталога, регистрирует их в очереди и удаляет с диска, исключая повторное чтение:


Процедура ЗагрузитьФайлыВОчередь() Экспорт

    КаталогОбмена = "\\Server\Exchange\Incoming\";
    МассивФайлов = НайтиФайлы(КаталогОбмена, "*.xml");
    
    Для Каждого НайденныйФайл Из МассивФайлов Цикл
        
        Блокировка = Новый БлокировкаДанных;
        ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ОчередьВходящихПакетов");
        ЭлементБлокировки.УстановитьЗначение("ИдентификаторПакета", НайденныйФайл.ИмяБезРасширения);
        
        НачатьТранзакцию();
        Попытка
            Блокировка.Заблокировать();
            
            ДвоичныеДанные = Новый ДвоичныеДанные(НайденныйФайл.ПолноеИмя);
            
            МенеджерЗаписи = РегистрыСведений.ОчередьВходящихПакетов.СоздатьМенеджерЗаписи();
            МенеджерЗаписи.ИдентификаторПакета = НайденныйФайл.ИмяБезРасширения;
            МенеджерЗаписи.ДанныеФайла = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
            МенеджерЗаписи.Статус = Перечисления.СтатусыОбработки.Новый;
            МенеджерЗаписи.ДатаСоздания = ТекущаяДатаСеанса();
            МенеджерЗаписи.Записать(Истина);
            
            УдалитьФайлы(НайденныйФайл.ПолноеИмя);
            
            ЗафиксироватьТранзакцию();
        Исключение
            ОтменитьТранзакцию();
            ЗаписьЖурналаРегистрации("Интеграция.Загрузка", УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        КонецПопытки;
        
    КонецЦикла;

КонецПроцедуры

Архитектурное решение 2: Распараллеливание обработки через пул фоновых заданий

Выясним, как ускорить разбор очереди. Вместо последовательной обработки в один поток запустим параллельные фоновые задания с помощью менеджера ФоновыеЗадания. Каждое фоновое задание будет брать свой отдельный пакет из очереди и обрабатывать его независимо от других.

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


Процедура ОбработатьОчередьПараллельно() Экспорт

    МаксимальноеЧислоПотоков = 4;
    
    // Получаем список активных фоновых заданий нашей обработки
    Фильтр = Новый Структура;
    Фильтр.Вставить("Ключ", "ОбработкаПакетаЗаказов");
    Фильтр.Вставить("Состояние", СостояниеФоновогоЗадания.Активно);
    
    АктивныеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(Фильтр);
    ДоступноПотоков = МаксимальноеЧислоПотоков - АктивныеЗадания.Количество();
    
    Если ДоступноПотоков <= 0 Тогда
        Возврат; // Все рабочие потоки заняты
    КонецЕсли;
    
    // Выбираем порцию новых пакетов из регистра сведений
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ " + ДоступноПотоков + "
        |	Очередь.ИдентификаторПакета КАК ИдентификаторПакета
        |ИЗ
        |	РегистрСведений.ОчередьВходящихПакетов КАК Очередь
        |ГДЕ
        |	Очередь.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыОбработки.Новый)
        |УПОРЯДОЧИТЬ ПО
        |	Очередь.ДатаСоздания ВОЗР";
        
    Результат = Запрос.Выполнить();
    Если Результат.Пустой() Тогда
        Возврат;
    КонецЕсли;
    
    Выборка = Результат.Выбрать();
    Пока Выборка.Следующий() Цикл
        
        // Переводим пакет в статус "ВРаботе"
        Если НЕ ЗахватитьПакетДляОбработки(Выборка.ИдентификаторПакета) Тогда
            Продолжить;
        КонецЕсли;
        
        // Запускаем независимый фоновый поток
        ПараметрыПроцедуры = Новый Массив;
        ПараметрыПроцедуры.Добавить(Выборка.ИдентификаторПакета);
        
        ПараметрыФонового = Новый Структура;
        ПараметрыФонового.Вставить("Наименование", "Обработка пакета " + Выборка.ИдентификаторПакета);
        
        ФоновыеЗадания.Выполнить(
            "ИнтеграцияСервер.ВыполнитьОбработкуПакета", 
            ПараметрыПроцедуры, 
            "ОбработкаПакетаЗаказов", 
            ПараметрыФонового.Наименование
        );
        
    КонецЦикла;

КонецПроцедуры

Функция ЗахватитьПакетДляОбработки(ИдентификаторПакета)
    
    Блокировка = Новый БлокировкаДанных;
    ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ОчередьВходящихПакетов");
    ЭлементБлокировки.УстановитьЗначение("ИдентификаторПакета", ИдентификаторПакета);
    
    НачатьТранзакцию();
    Попытка
        Блокировка.Заблокировать();
        
        МенеджерЗаписи = РегистрыСведений.ОчередьВходящихПакетов.СоздатьМенеджерЗаписи();
        МенеджерЗаписи.ИдентификаторПакета = ИдентификаторПакета;
        МенеджерЗаписи.Прочитать();
        
        Если МенеджерЗаписи.Выбран() И МенеджерЗаписи.Статус = Перечисления.СтатусыОбработки.Новый Тогда
            МенеджерЗаписи.Статус = Перечисления.СтатусыОбработки.ВРаботе;
            МенеджерЗаписи.Записать(Истина);
            ЗафиксироватьТранзакцию();
            Возврат Истина;
        Иначе
            ОтменитьТранзакцию();
            Возврат Ложь;
        КонецЕсли;
    Исключение
        ОтменитьТранзакцию();
        Возврат Ложь;
    КонецПопытки;
    
КонецФункции

Архитектурное решение 3: Оптимизация самого алгоритма обработки заказа

Проанализируем, почему создание и проведение одного документа может занимать до 4 минут. Для устранения задержек проверим следующие узкие места в коде процедуры ВыполнитьОбработкуПакета:

  1. Чтение данных внутри транзакции: Разбор XML/JSON файла и поиск ссылочных данных (номенклатура, контрагенты, договоры) должны выполняться до открытия транзакции записи документа. Время транзакции должно исчисляться миллисекундами.
  2. Поиск объектов в цикле: Нельзя выполнять запросы или методы НайтиПоНаименованию() / НайтиПоРеквизиту() внутри цикла обхода строк файла. Все сопоставления необходимо загружать одним пакетным запросом с помещением входных данных во временную таблицу (МенеджерВременныхТаблиц).
  3. Запись без проведения: Если бизнес-логика допускает отложенный контроль, создавайте документ в режиме РежимЗаписиДокумента.Запись, а проведение выполняйте отдельным общим регламентным заданием в наименее загруженные часы либо пакетом.
  4. Синхронные сетевые вызовы: Если во время проведения документа выполняются HTTP-запросы к внешним сервисам (проверка баланса, резервирование на внешнем складе), переведите их на асинхронный обмен, чтобы транзакция базы данных не простаивала в ожидании ответа по сети.

Сводная таблица подходов к обработке очередей

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

  1. Один последовательный фоновый поток: Просто в реализации, но не масштабируется при увеличении входящего потока данных. Подходит только для редких обменов.
  2. Пул фоновых заданий (воркеры): Обеспечивает параллельную утилизацию ресурсов сервера, легко масштабируется изменением количества потоков, предотвращает блокировку всей очереди из-за одного ошибочного файла.
  3. Переход на HTTP-сервисы 1С: Прием данных внешней системой напрямую по REST API с мгновенной регистрацией в очереди (время ответа < 0.2 сек) полностью исключает зависимость от файловой системы и сетевых папок.

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

← На главную