gifts2017

Пример механизма параллельной обработки данных

Опубликовал Михаил Гусев (Идальго) в раздел Программирование - Практика программирования

В статье представлена простая идея реализации параллельной обработки данных, базирующаяся на использовании фоновых заданий, а также паре регламентных заданий, которые осуществляют управление очередью. Материал статьи может быть полезен в случаях, когда вы хотите создать собственную систему управления параллельно выполняющимися заданиями, но в силу каких-то причин вам не подходит механизм регламентных заданий (например, если нужно ограничить число одновременно выполняющихся регламентных заданий, соблюдать очерёдность и приоритет их выполнения и т.п.). Статья ориентирована на клиент-серверный вариант работы 1С8, а приложенная конфигурация выполнена на обычных формах.

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

Изучая возможности работы с регламентными заданиями, я столкнулся с тем, что иногда, для балансировки нагрузки на сервере, необходимо ограничивать число одновременно выполняющихся заданий. Штатный механизм предоставляет, конечно, возможность управления стартом заданий по расписанию и т.п., но вот именно для управления балансировкой необходимо выдумывать велосипед. Собственно о таком велосипеде я и пишу. Сразу оговорюсь, что представленный механизм - лишь одна из многих вариаций на тему параллельной обработки данных и уж точно никаким образом не претендует на законченное решение. Впрочем, как мне кажется, сделав некоторые очевидные доработки, описанный механизм можно использовать и в рабочих базах.

Итак, суть механизма заключается в использовании двух регламентных заданий - Диспетчера и Менеджера заданий очереди (Эта идея зацепила меня при прочтении замечательной заметки на Хабре - https://habrahabr.ru/post/255387/).

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

Теперь по реализации.

1)В программе имеется справочник "Задания для обработки", в котором собственно и хранятся объекты обработки (т.е. задания). В принципе, такими объектами могут служить различные процедуры или программы загрузки/выгрузки, регламентные задания и прочее. Однако, поскольку это лишь основа механизма, то как таковых объектов обработки не задано, сейчас это вообще просто условный реквизит "Объект" строкового типа, а единственное действие, выполняемое при передаче программного кода такому объекту - минутное ожидание, т.е. пауза в 60 секунд. Понятное дело, если есть необходимость, чтобы задание при своём выполнении выполняло более изысканный код, то нужно соответствующим образом, в т.ч. используя реквизиты экземпляра элемента справочника "Задания для обработки", написать необходимый программный код. Тем не менее, для иллюстрации работы механизма, имеющихся сущностей вполне достаточно.

Каждый элемент справочника "Задания для обработки" имеет:

*флаг "Используется" (по сути это включение/выключение задания);

*флаг "Запускать в отдельном потоке" (задания с установленным флагом будут запускаться в отдельных фоновых заданиях, а задания со сброшенным могут запускаться последовательно по несколько штук в одном фоновом задании);

*поле "Приоритет выполнения" (чем меньше число (по умолчанию - 10), тем выше приоритет выполнения данного задания);

*поле "Идентификатор потока" (задания с одинаковыми идентификаторами потока будут выполняться в дном фоновом задании, при условии, что флаг запуска в отдельном потоке у таких заданий сброшен);

*поле "Объект" (Строка, но после небольших доработок, м.б. использован как идентификатор объекта, в который будет передаваться программный код для выполнения);

*поля "Наименование" и "Комментарий" (предназначение этих полей очевидно)

2)По расписанию, скажем каждые 5 минут, в системе запускается Диспетчер (регламентное задание "Диспетчер заданий"), который получает из справочника "Задания для обработки" все включенные задания, т.е. которые нужно выполнять, а затем, те из них, которых нет в очереди - туда и помещает. Более ничего Диспетчер не выполняет.

3)Как уже было отмечено выше, в механизме существует ещё и Менеджер (второе регламентное задание - "Менеджер заданий"), основной задачей которого является запуск заданий и очистка очереди (по окончанию выполнения заданий). Менеджер, как мне кажется, должен запускаться чаще, чем диспетчер, т.к. его работа напрямую влияет на скорость выполнения заданий очереди (у меня он запускается раз в минуту). Сразу после старта Менеджер получает параметры, определяющие количество потоков (одновременно выполняемых фоновых заданий) для выполнения разных видов заданий очереди. После этого, Менеджер удаляет из очереди отключенные задания (вдруг их кто-то выключил), а также удаляются (из очереди) выполняющиеся задания, по которым не найдено активных фоновых заданий. После корректировки очереди, Менеджер получает нераспределенные по потокам задания из очереди и распределяет их по потокам. Причем, распределив то, или иное задание, Менеджер сразу запускает его выполнение. Вначале распределяются задания, запускаемые в отдельных потоках. Делается это с учётом их приоритета и наличия вакантных ресурсов, т.е. лимита по количеству параллельно выполняющихся заданий данного вида (одно задание на один поток). Если такой лимит для старта задания отсутствует, то задание не распределяется (т.к. по сути в данный момент нет свободных ресурсов для его выполнения). Следом идёт очередь распределения заданий, которые могут быть объединены для выполнения в рамках одного фонового задания. Последовательность распределения таких заданий следующая - сначала распределяются задания с заполненными идентификаторами, а затем с пустыми. Распределение по потокам выполнения и в этом случае происходит с учётом приоритетов и наличия ресурсов. По окончании выполнения каждого из заданий, программа (точнее обработчик каждого из фоновых заданий) выполняет удаление соответствующего задания из очереди.      

В общем, это основные моменты, которые дают понимание моего "велосипеда".

На последок ещё раз отмечу, что это не готовое решение, а лишь пример, или если угодно - основа для построения более сложного механизма. Так, например, поскольку в регистр очереди довольно часто осуществляется запись, то для повышения производительности можно подумать об использовании при этом управляемых блокировок и транзакций. А если ещё и "запилить" к этому что-то вроде расписания выполнения заданий (из справочника "Задания для обработки"), а также систему логирования результатов их выполнения и сбора статистики(в т.ч. по загрузке ресурсов, времени ожидания и выполнения и т.п.), то можно получить интересное решение.

В добавление к указанной статье с Хабра, считаю необходимым указать ещё парочку:

http://infostart.ru/public/306865/
http://курсы-по-1с.рф/news/2015-12-04-threads-data/

Скачать файлы

Наименование Файл Версия Размер
Демобаза 2
.dt 53,23Kb
12.08.16
2
.dt гма1 53,23Kb Скачать

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Sergey Andreev (starik-2005) 13.08.16 10:31
А блокировку соседних заданий по критическому коду можно в 1С сделать?
2. Михаил Гусев (Идальго) 13.08.16 13:51
(1) starik-2005, думаю, что реализовать паузу в запуске соседних заданий совсем просто, но вот для того, чтобы уже выполняющиеся задания стопарнуть на время - тут сложнее )))
3. Sergey Andreev (starik-2005) 13.08.16 19:34
(2) Идальго, тогда Вам следует прочитать это.
4. Михаил Гусев (Идальго) 14.08.16 19:16
(3) starik-2005, спасибо, я прочитал. Я подобные вещи тоже давно использую (и этого всегда 100% хватало). В вышеприведенном комментарии я немного другое имел в виду, ведь если делать так, как предложено у вас, то для того, чтобы метод стал универсальным, то нужно либо в каждом задании прописывать подобный код, либо делать вызов какой-то общей процедуры, которая будет проверять мьютекс. Однако это не совсем универсально и удобно, т.к. этот код проверки выполняется каждым из заданий, а я подразумевал использование более универсальной штуки, которая бы подключалась к каждому из заданий и получала текущий статус его выполнения, и в случае ошибки, как-то это дело переваривала. Ну и в целом, у меня же здесь просто пример - основа, на которую можно посложнее штучки навернуть (кому нужно).
5. Артур Аюханов (artbear) 18.08.16 12:50
(0) А почему не взял готовый пример подсистемы от автора статьи с Хабра?
Код реально рабочий, проверен на нескольких системах в разных организациях.
Код открыт на Гитхабе, есть поддержка продукта.

Тем более, что в подсистеме использованы приемы, позволяющие решить некоторые возникающие проблемы с фоновыми заданиями.

Зачем тратить время на написание велосипедов?

6. Михаил Гусев (Идальго) 18.08.16 17:48
(5) artbear, Ну там немного по-другому сделано, но суть конечно та же самая. Вообще у всех подобных решений реализация очень похожа. Кроме того, в той статье сделано вроде на УФ, а мне нужно было на обычных. Да и Господи там писать то на пару-тройку часов, когда логику представляешь (собственно её я и подглядел на гитхабе, хотя потом ещё кучу мест нашел). Мучиться с разбором чужого кода не хотелось, да и просто интересно было))) Плюс, у меня же раскидывание по приоритетам и свободным потокам и т.п. есть, а на гитхабе не было (точно не помню уже). Да и вообще моя для понимания механизма, или как основа для более сложного продукта попроще и поудобнее будет, как мне кажется.
7. Sergey Andreev (starik-2005) 18.08.16 20:22
Я как-то многопоточный код замутил через произвольный алгоритм и функцию общего модуля "ВыполнитьАлгоритм". Если разобраться в механизме, то никаких подсистем не надо - все делается на коленке за двадцать минут.
8. Артур Аюханов (artbear) 19.08.16 17:47
(6) (7)
Если организовывать гарантированную доставку/выполнение, то пятью/двадцатью минутами не обойдешься.

Задумайтесь о следующем: как правило, все усложнения алгоритма/ПО возникают не из воздуха, а как результат решения каких-то бизнес-задач.
Например, в продукте Гитхаба решались задачи - гарантированная доставка, выполнение в высоконагруженной среде, блокировки/конфликты и т.п.

ЗЫ напомню, что я не автор продукта/статьи на Гитхабе/Хабре.
9. Артур Аюханов (artbear) 19.08.16 17:49
(6)
Кроме того, в той статье сделано вроде на УФ, а мне нужно было на обычных

В "той статье" на УФ сделано буквально 2 простые формы, практически без кода или только вызов серверных методов. Весь остальной код - это серверный код, работающий как в ОФ, так и в УФ.
10. Михаил Гусев (Идальго) 19.08.16 21:05
(8) artbear, Да никаких в той обработке (на гитхабе) особых штучек в плане работы с нагруженностью и гарантированностью нет. Как и в моей обработке, там есть ограничение на количество потоков и, вроде была удалялка зависших заданий. У меня же по части распределения задач - больше настроек. Работа по разруливанию блокировок(н-р регистров) и т.п., как я полагаю, также должна осуществляться со стороны кода запущенной обработки (т.е. это не менеджер очереди делает). Что касается сложности кода и времени разработки - обработки почти одинаковые, хотя мне мой код конечно понятнее))) Давайте закончим уже про ту обработку (обработки конечно похожи, но отличия тем не менее есть и я их описывал выше)?
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа