Применение параллельных алгоритмов в среде 1С Предприятия

в 13:29, , рубрики: 1c, github, Алгоритмы, параллельные алгоритмы, параллельные вычисления, Программирование, разработка, метки: ,

Наверное, каждый из нас сталкивался с ситуацией, когда нужно выполнить большой объем вычислений или передать/получить большой объем информации за ограниченный промежуток времени. А сколько из нас остановилось на последовательном алгоритме и закрыли глаза на продолжительность выполнения? Ну и что, что 20 часов ведется расчет/отправка/получение (подчеркнуть нужное) каких-то данных? Ну, я «выжал» из системы все, что можно, быстрее не получится… При этом серверное железо загружено на минимум.

На самом деле, почти всегда доступна альтернатива в виде распараллеливания выполняемой задачи. Конечно, параллельные алгоритмы несколько сложнее — балансировка нагрузки, синхронизации между потоками, а так же, в случае разделяемых ресурсов, борьба с ожиданием на блокировках и избегание deadlock’ов. Но, как правило, оно того стоит.

Об этом мы сегодня и поговорим… в контексте 1С Предприятия.

Практически во всех современных языках есть необходимый инструментарий для реализации параллелизма. Но не везде этот инструментарий удобен для использования. Кто-то скажет: «Ну и что тут сложного и не удобного? Платформенный механизм фоновых заданий в руки и вперед!». На практике оказалось не так все просто. А что если задача бизнес-критичная? Тогда нужно обеспечить, во-первых, гарантированное выполнение заданий и, во-вторых, мониторинг работы подсистемы выполнения заданий.

Ничего из этого механизм фоновых заданий не предоставляет.
Фоновое может “упасть” в любой момент. Вот только небольшой список возможных ситуаций:

  • администратор для проведения регламентных работ завершил все сессии;
  • внешний ресурс, требуемый на момент выполнения задания, был недоступен в течении какого-то времени;
  • у части заданий после очередного обновления могут появится ошибки в коде, фоновое будет остановлено по эксепшену и, кроме записи в журнал регистрации, никому ничего не сообщит.

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

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

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

Область применения

  • Использование параллельных вычислений в автоматизации бизнес процессов;
  • Параллельное выполнение запросов в долгих отчетах/обработках;
  • Процессы загрузки/выгрузки данных;
  • Организация нагрузочного тестирования.

Основной принцип работы

image

Добавляется задание очень просто — нужно указать точку входа (путь к экспортному методу с серверным контекстом в общем модуле или модуле менеджера) и кортеж с параметрами в виде структуры:

мзЗадания.ДобавитьЗадание("Тестирование.Уснуть", Новый Структура("Секунды", 3));

При этом происходит запись в регистр сведений мзЗадания. Первоначально задание имеет состояние Ожидает. Метод возвращает КлючЗадания для возможного отслеживания прогресса в основном клиентском потоке. Кортеж с параметрами так же расширяется свойством КлючЗадания, чтобы при выполнении задания был контекст, он иногда может быть полезен.

Далее, раз в минуту просыпается Менеджер, который первым делом проверяет исполнителей и освобождает задания, исполнители которых «умерли». Проверка заключается в следующем. Если задание в состоянии Выполняется и прописанное в качестве исполнителя фоновое не активно (удалено администратором, возникла исключительная ситуация в коде и т.д.), то задание возвращается в очередь. Для этого у задания выставляется состояние Ожидает.

Следующим шагом запрашиваются из очереди задания в соответствии с настройками балансировки нагрузки (сейчас это только ограничение на количество одновременно работающих исполнителей).

После этого Менеджер под каждое задание запускает Исполнителя. В качестве исполнителей выступают те самые фоновые задания платформы 1С.

При запуске Исполнитель делает отметку, что взял задание в работу — прописывает свой уникальный идентификатор у задания в свойстве КлючИсполнителя и выставляет состояние задания в Выполняется.

Когда задание выполнено, Исполнитель так же делает соответствующую отметку в задании — состояние Выполнено.

Для использования подсистемы в оперативном режиме есть метод ДобавитьЗаданиеВнеОчереди. При этом сразу запускается Исполнитель, минуя Менеджера, и забирает новое задание. Сигнатура такая же, как у основного метода ДобавитьЗадание. На такие задания ограничение на количество исполнителей НЕ распространяется, НО квота используется.

Так же есть возможность отменять задания в очереди методом ОтменитьЗадание. При этом отменять можно только задания, находящиеся в состоянии Ожидает. Уже запущенные задания не отменяются, поскольку:

  1. У фоновых заданий вроде как есть метод отмены, но он работает для меня не прозрачно. Очень часто наблюдал картину, когда длительное фоновое задание дорабатывало до конца несмотря на отправленную команду отмены;
  2. Не хочется оставлять систему в неконсистентном состоянии. Кто знает, как именно написан код задания и что он делает в базе данных?

Для удобного управления рабочим процессом предусмотрены следующие методы:

  • ДождатьсяВыполнения(КлючиЗаданий, Таймаут = 5) — усыпляет текущий поток до выполнения указанного списка заданий, либо до истечения указанного времени;
  • ОжидатьСостояниеЗадания(КлючЗадания, ОжидаемоеСостояние, Таймаут = 5) — усыпляет текущий поток до установления указанного состояния у задания, либо до истечения указанного времени;
  • ОжидатьИзмененияСостояния(КлючЗадания, ТекущееСостояние, Таймаут = 5) — усыпляет текущий поток до изменения состояния у задания с указанного на любое другое, либо до истечения указанного времени.

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

image

Настройка

Для настройки есть специальная обработка «Управление менеджером заданий»:

image

Доступные настройки:

  • Ограничение на количество исполнителей — для балансировки нагрузки на сервере 1С. Принимает значение от 0 до 9999. При значении 0 задания в работу браться не будут.
  • Глубина хранения истории (дни) — если указано значение отличное от 0, тогда подсистема сама будет чистить информацию по старым выполненным заданиям оставляя последние N дней указанных в настройке.

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

Тут же есть поверхностный мониторинг, который может показать, насколько система справляется с заданиями. Показатели:

  1. Количество активных исполнителей;
  2. Количество заданий в очереди (в состоянии Ожидает);
  3. Всего активных заданий (п.1 + п.2).

Показатели автоматически обновляются раз в минуту. Можно явно обновить, нажав на соответствующую кнопку. При желании, можно заглянуть в сам регистр заданий. Там есть возможность посмотреть, когда задания брались в работу и как долго выполнялись, там же есть счетчик количества попыток выполнения задания.

Расширенный мониторинг сильно зависит от области применения и оставлен на откуп потребителям подсистемы.

Примеры использования

Использование параллельных вычислений в автоматизации бизнес процессов

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

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

Параллельное выполнение запросов в долгих отчетах/обработках

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

Процессы загрузки/выгрузки данных

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

Организация нагрузочного тестирования

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

Исходники и прочее

Подсистема доступна на github/TaskManagerFor1C. CF файл открыт, так что можно ознакомиться с исходными кодами.

Подсистема разрабатывалась через тестирование (TDD), тесты доступны во внешней обработке /Тесты/Тесты_МенеджерЗаданий.epf. Для запуска тестов нужен инструментарий xUnitFor1C.

Обратная связь приветствуется. С удовольствием отвечу на все возникшие по инструменту вопросы.

Автор: wizi4d

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js