Внешняя обработка в фоне: общий модуль, БСП и контроль выполнения
Содержание
Фоновое задание из обработки запускают на сервере методом ФоновыеЗадания.Выполнить("ИмяМодуля.ИмяПроцедуры", Параметры). Процедура должна быть экспортной и лежать в неглобальном серверном общем модуле. Метод модуля объекта, а тем более модуля формы обработки, напрямую в фон не передать. Поэтому для внешней обработки нужен посредник: общий модуль в конфигурации или расширении, который в сеансе задания заново подключит обработку и вызовет её процедуру.
Если в базе есть БСП (Бухгалтерия 3.0, ЗУП 3.1, УТ 11 и другие типовые), есть путь без программирования посредника: зарегистрировать обработку как дополнительную с командой «Вызов серверного метода». Какой способ когда выбирать — в таблице.
| Способ | Когда подходит | Что нужно | Ограничения |
|---|---|---|---|
ФоновыеЗадания.Выполнить + общий модуль | Своя конфигурация, полный контроль над запуском и прогрессом | Общий модуль в конфигурации или расширении | В типовой без расширения модуль не добавить |
| Дополнительная обработка БСП, команда «Вызов серверного метода» | Типовая конфигурация, нужен запуск по кнопке или по расписанию | Функция СведенияОВнешнейОбработке() в модуле объекта | Нужны права на дополнительные обработки |
ДлительныеОперации БСП | Обработка встроена в конфигурацию | Модуль ДлительныеОперации вашей версии БСП | Сигнатуры различаются в версиях БСП 2.x и 3.x |
Как платформа выполняет фоновое задание
Фоновое задание — это отдельный сеанс информационной базы без интерфейса. В клиент-серверном варианте он работает в рабочем процессе кластера, в файловом — внутри процесса приложения, которое его запустило.
Из этого следуют правила, которые объясняют почти все проблемы:
- 🧩 Отдельный сеанс. Всё, что было в сеансе пользователя, в задании отсутствует: подключённые внешние обработки, значения реквизитов формы, параметры сеанса, если их не установили заново.
- 🔐 Права пользователя. Задание работает от имени того, кто его запустил, с его ролями.
- 🖥️ Только сервер. Нельзя открыть форму, задать вопрос, показать предупреждение или диалог выбора файла. Пути к файлам — это пути на сервере 1С, а доступ к ним определяется учётной записью службы (по умолчанию
USR1CV8). - 📦 Параметры передаются копией. В задание уходят только сериализуемые значения: примитивы, структуры, массивы, ссылки, таблицы значений, двоичные данные. Объект обработки, данные формы и COM-объекты передать нельзя.
- ⏳ Асинхронность.
Выполнитьвозвращает объектФоновоеЗаданиесразу, не дожидаясь окончания работы.
В файловой базе задание живёт внутри процесса клиента. Закрыли 1С — задание прервалось. В клиент-серверной базе оно продолжит работу и после закрытия формы или выхода пользователя.
Что подготовить в обработке и конфигурации
Для схемы с ФоновыеЗадания.Выполнить нужны три части.
- Общий модуль-посредник. Создайте в конфигурации или в расширении общий модуль, например
ФоновыеВнешниеОбработки. Установите флаг «Сервер», флаги «Клиент» и «Глобальный» снимите. Флаг «Вызов сервера» не нужен: модуль вызывается из серверной процедуры формы. - Экспортная процедура в модуле объекта обработки. Её вызовет посредник. Назовите её одинаково во всех своих фоновых обработках, например
ВыполнитьВФоне, — посредник станет универсальным. - Форма с командой запуска и реквизитами для идентификатора задания и адреса результата.
В типовой конфигурации на поддержке общий модуль удобнее добавить расширением. Конфигурация не снимается с поддержки, обновления ставятся как обычно. Порядок такой: Конфигурация → Расширения конфигурации, добавить расширение, создать в нём общий модуль, затем сохранить расширение в базу.
Расширение с общим модулем меняет конфигурацию базы. Делайте это вне рабочего времени и после резервной копии.
Как передать внешнюю обработку в задание
Сеанс задания не знает об обработке, открытой у пользователя. Ему нужно отдать двоичные данные файла .epf, а он сам подключит её через ВнешниеОбработки.Подключить.
Получить двоичные данные на сервере можно из свойства ИспользуемоеИмяФайла объекта обработки: при открытии файла платформа держит его копию, доступную серверу. Если в вашей схеме путь недоступен, передайте файл с клиента через НачатьПомещениеФайлаНаСервер и работайте с адресом во временном хранилище.
Код запуска через ФоновыеЗадания.Выполнить
Начнём с посредника. Он получает файл обработки и параметры, подключает обработку в сеансе задания и вызывает её экспортную процедуру.
// Общий модуль ФоновыеВнешниеОбработки (флаг «Сервер»)
Процедура ВыполнитьВнешнююОбработку(ДвоичныеДанныеОбработки, ПараметрыЗадания, АдресРезультата) Экспорт
АдресОбработки = ПоместитьВоВременноеХранилище(ДвоичныеДанныеОбработки);
// Третий параметр — безопасный режим. Истина запрещает работу с файлами, COM и внешними компонентами
ИмяОбработки = ВнешниеОбработки.Подключить(АдресОбработки, , Истина);
Обработка = ВнешниеОбработки.Создать(ИмяОбработки);
Обработка.ВыполнитьВФоне(ПараметрыЗадания, АдресРезультата);
КонецПроцедуры
Теперь модуль формы обработки. Команда «Запустить» готовит адрес результата и стартует задание, обработчик ожидания следит за ним.
// Модуль формы. Реквизиты формы: ИдентификаторЗадания (УникальныйИдентификатор), АдресРезультата (Строка)
&НаКлиенте
Процедура Запустить(Команда)
ЗапуститьНаСервере();
ПодключитьОбработчикОжидания("ПроверитьЗадание", 2);
КонецПроцедуры
&НаСервере
Процедура ЗапуститьНаСервере()
АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор);
ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
ДвоичныеДанные = Новый ДвоичныеДанные(ОбработкаОбъект.ИспользуемоеИмяФайла);
ПараметрыЗадания = Новый Структура;
ПараметрыЗадания.Вставить("ДатаНачала", Объект.ДатаНачала);
ПараметрыЗадания.Вставить("ДатаОкончания", Объект.ДатаОкончания);
Параметры = Новый Массив;
Параметры.Добавить(ДвоичныеДанные);
Параметры.Добавить(ПараметрыЗадания);
Параметры.Добавить(АдресРезультата);
Задание = ФоновыеЗадания.Выполнить(
"ФоновыеВнешниеОбработки.ВыполнитьВнешнююОбработку",
Параметры,
"ПересчетЦен", // ключ: не даст запустить второй экземпляр
"Внешняя обработка: пересчёт цен"); // наименование в списке заданий
ИдентификаторЗадания = Задание.УникальныйИдентификатор;
КонецПроцедуры
Строку с именем метода пишите точно: ИмяОбщегоМодуля.ИмяПроцедуры, без скобок и параметров. Параметры передаются массивом в том порядке, в котором объявлены в процедуре.
Зачем адрес результата создают в форме
Задание не может вернуть значение напрямую. Обычный приём — создать в сеансе пользователя адрес во временном хранилище, привязанный к УникальныйИдентификатор формы, и передать его в задание. Задание записывает результат по этому адресу, форма забирает. Хранилище очистится само, когда закроют форму.
Логика обработки внутри фонового задания
Модуль объекта обработки содержит саму работу. Здесь удобно сообщать о прогрессе: сообщения, выведенные в задании, накапливаются и забираются формой.
// Модуль объекта обработки
Процедура ВыполнитьВФоне(ПараметрыЗадания, АдресРезультата) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа
| И НЕ Номенклатура.ПометкаУдаления";
Выборка = Запрос.Выполнить().Выбрать();
Всего = Выборка.Количество();
Обработано = 0;
Пока Выборка.Следующий() Цикл
// ... полезная работа с Выборка.Ссылка ...
Обработано = Обработано + 1;
Если Обработано % 500 = 0 Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон("Обработано %1 из %2",
Формат(Обработано, "ЧГ=0"), Формат(Всего, "ЧГ=0"));
Сообщение.Сообщить();
КонецЕсли;
КонецЦикла;
ПоместитьВоВременноеХранилище(Новый Структура("Обработано", Обработано), АдресРезультата);
КонецПроцедуры
Пример: в справочнике 12 400 позиций, сообщение уходит каждые 500. Форма увидит 24 сообщения: последнее — «Обработано 12000 из 12400». Финальную цифру 12 400 вернёт структура результата.
Не вызывайте в этой процедуре формы, Вопрос, Предупреждение и диалоги выбора файла — задание упадёт с ошибкой, потому что интерфейса у него нет.
Если процедура пишет файлы или работает с COM, безопасный режим при подключении придётся отключить (Ложь в третьем параметре Подключить). Это требует административных прав и включает защиту от опасных действий. Отключайте только для своих проверенных обработок.
Как отслеживать и останавливать задание
Обработчик ожидания раз в 2 секунды спрашивает сервер о состоянии и забирает накопленные сообщения.
&НаКлиенте
Процедура ПроверитьЗадание()
Состояние = СостояниеЗадания(ИдентификаторЗадания);
Если Состояние = "Активно" Тогда
Возврат;
КонецЕсли;
ОтключитьОбработчикОжидания("ПроверитьЗадание");
Если Состояние = "Завершено" Тогда
Результат = ПолучитьИзВременногоХранилища(АдресРезультата);
ПоказатьПредупреждение(, "Готово. Обработано: " + Формат(Результат.Обработано, "ЧГ=0"));
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция СостояниеЗадания(Идентификатор)
Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Идентификатор);
Если Задание = Неопределено Тогда
Возврат "НеНайдено";
КонецЕсли;
Для Каждого Сообщение Из Задание.ПолучитьСообщенияПользователю(Истина) Цикл
Сообщение.Сообщить();
КонецЦикла;
Если Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
Возврат "Активно";
ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.Завершено Тогда
Возврат "Завершено";
ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.ЗавершеноАварийно Тогда
ОбщегоНазначения.СообщитьПользователю(ПодробноеПредставлениеОшибки(Задание.ИнформацияОбОшибке));
Возврат "Ошибка";
КонецЕсли;
Возврат "Отменено";
КонецФункции
Если в базе нет БСП, замените ОбщегоНазначения.СообщитьПользователю на создание СообщениеПользователю, как в модуле объекта выше.
Параметр Истина в ПолучитьСообщенияПользователю удаляет полученные сообщения, иначе форма будет показывать их повторно при каждой проверке.
Отмена и поиск заданий
- ⛔ Отменить: найдите задание по идентификатору и вызовите
Задание.Отменить(). Отмена не мгновенная: задание остановится, когда платформа сможет его прервать. Транзакция, начатая заданием, откатится. - 🔎 Найти все свои задания:
ФоновыеЗадания.ПолучитьФоновыеЗадания(Новый Структура("Ключ, Состояние", "ПересчетЦен", СостояниеФоновогоЗадания.Активно)). - ⏱️ Дождаться синхронно на сервере:
Задание.ОжидатьЗавершенияВыполнения(Таймаут). Метод доступен в платформе с номером не ниже 8.3.13; в более ранних использовалсяОжидатьЗавершения.
Без программирования задания видно в типовых конфигурациях на БСП: Администрирование → Обслуживание → Регламентные операции → Регламентные и фоновые задания. В клиент-серверной базе сеанс задания также виден в консоли кластера в списке сеансов информационной базы.
Завершение сеанса фонового задания в консоли кластера обрывает операцию на середине, а незавершённая транзакция откатывается. Если задание массово меняет данные, лучше дать ему закончить или отменить его кодом.
Вариант для типовой конфигурации через дополнительные обработки
Когда посредника в конфигурацию добавлять не хочется, используйте механизм дополнительных обработок БСП. Платформа сама выполнит серверную команду на сервере и даст поставить её на расписание.
- В модуле объекта обработки опишите экспортную функцию
СведенияОВнешнейОбработке(). В ней укажите вид «Дополнительная обработка» и команду с типом использования «Вызов серверного метода». Структуру описания удобно получить функциейДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке()из той же БСП. - В том же модуле создайте экспортную процедуру
ВыполнитьКоманду(ИдентификаторКоманды, ПараметрыВыполненияКоманды)— в ней и будет вся работа. - Зарегистрируйте файл:
Администрирование → Печатные формы, отчеты и обработки → Дополнительные отчеты и обработки, кнопка «Добавить из файла». - На вкладке команд в карточке задайте размещение в разделе. Для серверной команды там же настраивается расписание — тогда она будет выполняться регламентным заданием без пользователя.
Плюс схемы — не нужны ни расширение, ни общий модуль. Минус — меньше контроля над прогрессом, а обработку придётся перерегистрировать после каждого изменения файла.
Почему задание не запускается или падает
Как ускорить фоновую обработку больших объёмов
Одно задание обрабатывает данные в один поток. Когда объектов десятки тысяч, помогают такие приёмы:
- ✂️ Делите на порции. Выберите ссылки запросом, разбейте на части по 1 000–5 000 и запустите несколько заданий с разными порциями. Ограничьте число одновременных заданий: 4 параллельных задания на сервере с 4 ядрами обычно дадут больше, чем 20.
- 🔒 Короткие транзакции. Одна транзакция на весь объём держит блокировки и мешает пользователям. Записывайте каждый объект или пачку в своей транзакции.
- ⚔️ Порции без пересечений. Если два задания пишут в одни и те же регистры с одинаковыми измерениями, они ждут друг друга на управляемых блокировках. Делите данные по организациям, складам или периодам.
- 📉 Не грузите лишнее. Вместо
Ссылка.ПолучитьОбъект()ради чтения одного реквизита берите его запросом. Объект получайте, только когда его надо записать. - 🕒 Реже опрашивайте задание. Проверка раз в 1–2 секунды не нагружает сервер, а опрос каждые 0,1 секунды создаёт лишние серверные вызовы. Для долгих операций интервал можно увеличивать.
- 🌙 Тяжёлое — по расписанию. Пересчёты на сотни тысяч записей ставьте регламентным заданием на ночь.
Для параллельных порций родительское задание запускает дочерние и ждёт их через ФоновыеЗадания.ОжидатьЗавершенияВыполнения(МассивЗаданий) (платформа с номером не ниже 8.3.13). После ожидания проверьте состояние каждого дочернего: одно аварийное не остановит остальные.