Здравствуйте, сегодня я хотел бы рассказать про свой путь написания оркестратора для оркестратора UiPath. Поведать, какая была первопричина и во что это вылилось.
Как вводную, расскажу про компанию UiPath и ее основной продукт: среда разработки и выполнения программых роботов(далее АРМ). Изначально, продукт разрабатывался как инструмент UI-тестирования, как для web, так и для desktop приложений. Продукт был неплох, но как инструмент для тестирования нужен небольшому кругу лиц, тогда у менеджеров компании появилась идея продавать как платформу для роботизации бизнес процессов. Суть и там и там сводится к имитации действий пользователя, только в первом случае мы это делаем в тестовом окружении, а во втором - в реальной системе. Для управления кучей программных роботов за отдельные деньги, покупается лицензия на оркестратор.
Стоит рассказать про лицензирование. Среда выполнения программных роботов делилась на такие типы как: Attended (выполняется на машине пользователя, запуск производится через трей, запуск через поставляемый компанией оркестратор невозможен) и UnAttended (чаще всего выполняется на виртуальной машине, есть возможность запуска через поставляемый оркестратор). Есть ещё типы лицензий: Named User (выполнение только под одной уникальной доменной записью на одном АРМ) и Concurrent (одна доменная учётная запись может одновременно использоваться на нескольких АРМ). Некоторые вышеуказанные типы комбинируются между собой, соответственно и разная цена на них. Так UnAttended Concurrent - самая дорогая из них, а Named User(Attended) - самая дешевая. Разница в цене между ними вполне достигала 5-7 раз и это все цена на одно рабочее место. Необходимо учитывать, что лицензия покупается на год, но в течении года потребность в лицензиях разная. Более того, потребность разная и в течении недели. Так как автоматизируем действия пользователя, то чаще всего и запускаем роботов в это время (с 9 до 18) Соответственно, если считать только рабочее время, то робот работает 50% времени, а в остальное простаивает, но счётчик, как в такси, тикает.
Теперь поговорим про поставляемый не бесплатный оффлайн оркестратор, который представлял из себя web-приложение базирующееся на IIS и MSSQL Server. На момент написания своего оркестратора функционал поставляемого был сильно ограничен. Не было триггера на внешние события, приходилось писать робота, который периодически запускался и проверял на возникновение этих событий. Запуск роботов был возможен только по времени или появлению элементов в той или иной очереди. Был и "псевдо ИИ", который анализировал кол-во элементов в очереди и время, к которому необходимо завершить работу, но данный функционал был неактуален в "спокойные периоды" и не работал в "пиковые", так как расчет базировался на среднем выполнении элемента очереди за весь используемый ранее период. Т.е если обычно выполнение элемента очереди занимало 1 минуту, а в пики 10, то среднее могло быть 2. Для всех роботов в независимости от типа лицензии оркестратор выступает и как система сбора и хранения логов и как база очередей и хранилище лицензий.
С учётом всего вышеописанного было принято решение написать поверх свой оркестратор. Немного оффтопа: в кругах роботизаторов, у всех есть процесс, который именуется Бендером. В нашем случае это был самописный оркестратор, который дергал WebAPI-методы штатного, таким образом полностью управляя им.
В первую очередь, необходимо было заставить запускаться лицензии не привлекая оркестратор. За это отвечает uirobot.exe и передаваемые ему аргументы.
Во вторую очередь, необходимо сделать активным рабочий стол. Для этой цели, как временное решение подошло: TightVNC. Для активации рабочего стола после штатных перезагрузок был написан робот работающий через изображения, так как разбираться с WinAPI на тот момент не было времени. Тут многие скажут, а как быть если роботы работают одновременно на одной виртуальной машине Windows Server под разными УЗ. Мы ушли от этой практики, так как поведение работающих процессов иногда становилось непредсказуемым.
Для возможности удаленного запуска, был написан сервис на С# с использованием WCF и базовой авторизацией, обернутой в службу Windows, под рукой был ранее написанный шаблон. На данном этапе выяснилось, что uirobot.exe должен выполняться с дескриптором рабочего стола, иначе работающий робот не видит сетевых папок. Галочка у служб: "разрешить взаимодействие с рабочим столом не работает с времен Windows 8". Пришлось ковырять WinAPI.
Разработка роботов ведётся в специальной студии: UiPath Studio, тоже не бесплатная, в основе которой лежит технология Microsoft Workflow Foundation, или блочное программирование. Один из таких блоков: "Should Stop" у UiPath отвечает за информирование работающего процесс о необходимости дойти до некоторой логической точки и остановиться. Данный компонент не работает на Attended лицензиях и соответственно пришлось писать свой компонент реализующий данный функционал и WCF-сервис в составе нового оркестратора. У UiPath был неплохой компонент по работе с soap-запросами и годился для целей отладки, поэтому отчасти и был использован WCF.
Переходим к самому сложному, реализации оркестратора. Так как лицензии были в наличии разного типа, то запуск и остановка UnAttended лицензий производилась подачей команд на штатный оркестратор. Управление лицензиями Attended производилось через самописную службу WCF. На реализацию фронта времени не было, поэтому все управление производилось изменением в нескольких таблиц PostgreSQL. Данная БД была выбрана, так как она бесплатная, имелся неплохой опыт работы с ней и штатная возможность хранения нескольких значений в одной ячейке. В роли фронта отыгрывал DBeaver, причем, очень успешно. Кустарный оркестратор использовал всего около 10-15 команд штатного оркестратора: получение токена авторизации, список очередей и кол-ва элементов в них, история запущенных процессов для каждого робота (10 последних событий), соответствие окружений(Enviroment) и процессов(ProcessName). Последнее необходимо, так как для запуска необходимо указывать связку: Enviroment_ProcessName. Сначала, самонаписный оркестратор получал всю вышеуказанную информацию от штатного, затем проходил по таблице процессов в бд postgresql и смотрел: на очереди, время запуска и остановки, и отбирал подходящие. В первую очередь, приоритет имели задачи только по времени запуска, затем уже по времени и элементам в очередях. Анализировал простаивающие АРМ на предмет крайней перезагрузки и в случае необходимости перезагружал. Опытным путем было установлено, что перезагрузка каждые 24 часа благотворно влияют на стабильность поведения Windows машин. Затем он пробегался по АРМ и давал команду на остановку тем процессам, которые работают больше положенного. Затем, анализировал кол-во уже работающих по данному процессу с предельно допустимым кол-вом, да бы не случилась ситуация, когда все роботы заняты одним процессом. После чего уже производил запуск. Чтобы не перегружать штатный оркестратор запросами, собственноручно написанный запускался каждые: 2.5 - 5 минут и этого было достаточно, так как он позволял минимизировать холостые запуски процессов. Для получения всей информации из штатного оркестратора для всех очередей и АРМ необходимо было вызвать около: 500-1000 запросов. Все запущенные процессы велись в одной таблице postgresql, после остановки переносились в историческую таблицу и использовались в дальнейшем для анализа.
Возможности оркестратора
В случае, если процесс завершался ошибкой Failture(вылетел с Exception), то данная машина перезагружалась, а другая из имеющихся свободных занимала ее место.
Если процесс завершался со статусом Stopped или Sucсessful, то в указанный в таблице промежуток больше не запускался, так как считалось, что повторный запуск будет холостым. У Attended лицензий, кстати не мог быть статус Stopped.
Можно было выстраивать цепочки процессов. Когда после полной остановки всех машин по первому процессу, производился запуск второго. Использовался, когда разные процессы не должны пересекаться, так как было взаимовлияние в пользовательской программе.
В качестве запуска или остановки для процесса можно было указать не одну, а несколько непустых очередей или наоборот, когда все из указанных должны быть пустыми. Актуально для больших процессов, где используется последовательно несколько очередей, но работу в течении дня хотим разбить на несколько шагов. Иначе, необходимо весь программный код делить на эти шаги, что усложняет дальнейшую поддержку.
Для начала работы того или иного процесса можно было добавлять внешний триггер: к примеру статус чужой системы в ее БД.
Добавлена блокировка запуска по шаблону названия процесса на определенную дату или день недели. Актуально, когда праздничные дни выпадают на будни(в эти дни чаще роботы не работали). Когда у нас есть большие процессы, которые все же необходимо разбить на несколько программных роботов и тогда у них есть общий префикс названия.
Для работающих процессов проверял время последней записи в лог, если оно превышало некоторое значение, указанное в настройках, то считалось, что робот завис и данный процесс убивался с дальнейшей перезагрузкой виртуальной машины.
Также для процессов можно было указать время после которого необходимо было остановиться 'по-доброму', и если процесс спустя некоторое время не останавливался, то процесс убивался 'по-плохому', как в пункте 6.
Основная часть проекта была написана за 1.5 - 2 месяца. Стабильность работы данного оркестратора была в районе: 99,99%. При этом, несмотря на добавления ещё одного узла(самописный оркестратор) общая стабильность системы выросла. Самописный оркестратор позволил более правильно расставлять приоритеты у процессов, лучше комбинировать их, минимизировать холостые запуски. В момент внедрения позволил сократить потребность в лицензиях на 15%. Одновременно спокойно мог управлять 20 лицензиями, больше просто не было.
Главный вопрос: Насколько все это было легально. Я не юрист, но в лицензионном соглашении никаких пунктов о недокументированных возможностях или запрете на использование определенных лицензий другим образом не увидел.