ProActive

ProActive — открытое программное обеспечение для оркестрации рабочих нагрузок в корпоративных системах, разрабатываемое как часть сообщества OW2. Программная модель «workflow» позволяет задавать последовательности исполняемых файлов или скриптов на любом языке вместе с их зависимостями. Это позволяет ProActive выполнять функции планировщика и оркестратора заданий, оптимизируя использование вычислительных ресурсов.

ProActive построен на основе паттерна проектирования «активный объект», что способствует эффективному распределению задач и устойчивости к сбоям.

Общие сведения
ProActive
Тип планировщик заданий
Разработчики ActiveEon, Inria, OW2 Consortium
Написана на Java
Операционная система кроссплатформенный
Последняя версия 10.0 (F-Zero) (12 июля 2019)
Лицензия AGPL
Сайт activeeon.com

Основные возможности ProActive

  • Использование workflow для параллелизации задач на Java, скриптах или нативных исполняемых файлах, выполнение их на ресурсах с учётом различных ограничений (например, поддержка GPU, локальность библиотек или данных).
  • Веб-интерфейсы для проектирования и запуска рабочих процессов и управления вычислительными ресурсами. Для интеграции с корпоративными приложениями доступен RESTful API.
  • Вычислительные ресурсы могут объединяться (облако, кластеры, виртуализированная инфраструктура, настольные ПК) в единую виртуальную инфраструктуру. Реализована поддержка авто-масштабирования и стратегий управления ресурсами.
  • Обеспечена совместимость с гетерогенными workflow, где задачи могут выполняться на разных платформах, включая Windows, Mac и Linux.

Фреймворк ProActive на Java и модель программирования

Модель была разработана профессором Университета Ниццы София-Антиполис Дени Каромелем[1]. Дальнейшее развитие модели осуществлялось участниками команды OASIS в INRIA[2]. В книге «A Theory of Distributed Objects» представлен формализм ASP, описывающий модель ProActive и формальные семантики выполнения программ, а также свойства исполнения программ на этой платформе[3].

Активные объекты

Активные объекты — это базовые единицы активности и распределённости для построения конкурентных приложений на основе ProActive. Активный объект работает в собственном потоке; этот поток исполняет только методы, вызванные на этом объекте другими активными объектами, а также методы пассивных объектов, относящихся к его подсистеме. Благодаря ProActive, программисту не требуется явно создавать объекты Thread, как в стандартном Java.

Активные объекты могут запускаться на любом из доступных узлов. После создания активного объекта его активность (наличие потока) и размещение (локальное или удалённое) становятся полностью прозрачными. С любым активным объектом можно работать так же, как с пассивным экземпляром того же класса.

Активный объект состоит из двух компонентов: «тело» и стандартный объект Java. Тело объекта недоступно извне.

Тело отвечает за приём вызовов (или «запросов») к активному объекту и сохраняет их в очереди ожидания. Выполнение запросов происходит согласно заданной политике синхронизации; если политика не указана, используется стандартная очередь FIFO («первым пришёл — первым обслужен»).

Поток активного объекта выбирает из очереди запросов метод и выполняет его. Внутри активного объекта параллелизма не предусмотрено — это принципиальный момент архитектуры ProActive, необходимый для корректной работы предусловий и инвариантов класса.

В подсистеме, отправляющей вызов активному объекту, его представление осуществляется через прокси-объект. Прокси создаёт объекты-«фьючерсы» для будущих значений, преобразует вызовы в Request-объекты (метаобъектная техника — рефикация), а также производит полное копирование (deep copy) пассивных объектов, передаваемых в качестве параметров.

Базовые принципы активных объектов

ProActive реализует библиотеку для разработки приложений на модели Eiffel// — параллельного расширения языка Eiffel.

В этой модели приложения строятся из подсистем, каждая из которых содержит один активный объект (и, соответственно, один поток) и произвольное число пассивных объектов. Между подсистемами не допускается совместное использование пассивных объектов.

Эти особенности влияют на топологию приложения: из всех объектов подсистемы (активного и пассивных) только активный объект видим внешним объектам. При этом и активные, и пассивные объекты могут хранить ссылки на активные объекты. Если объект o1 ссылается на пассивный объект o2, то оба находятся в одной подсистеме.

undefined

Это влияет и на семантику обмена сообщениями между подсистемами. Если при вызове метода активного объекта в качестве параметра передаётся ссылка на пассивный объект, то пассивный объект всегда копируется полностью (deep-copy), чтобы избежать «разделяемых пассивных объектов» между подсистемами. Активные объекты, напротив, всегда передаются по ссылке; аналогичное правило действует и для возвращаемых объектом значений.

Благодаря использованию асинхронных вызовов, объектов-фьючерсов и отсутствию разделяемых данных, приложения на ProActive не требуют перестройки архитектуры — по сути, их код практически не меняется вне зависимости от того, работают они в последовательной, многопоточной или распределённой среде.

Асинхронные вызовы и объекты-фьючерсы

По возможности вызовы методов у активных объектов реализуются как асинхронные запросы (рефикация). Если нет — вызов выполняется синхронно и блокирует вызывающий поток до получения ответа. При асинхронном вызове вызывающая сторона немедленно получает объект-фьючерс.

Объект-фьючерс служит контейнером для результата ещё невыполненного вызова метода. Это позволяет вызывающему потоку продолжать работу до момента, пока не потребуется результат вызова. Если результат необходим, а выполнение метода ещё не завершено, вызывающий поток автоматически блокируется до появления результата. Хотя фьючерс по структуре напоминает активный объект, он таковым не является: состоит только из заглушки (Stub) и прокси.

Пример кода

Фрагмент ниже иллюстрирует использование объектов-фьючерсов. Предположим, пользователь вызывает методы foo и bar у активного объекта a; foo возвращает void, а bar — объект класса V:

// асинхронный односторонний вызов активного объекта (AO) a
a.foo(param);

// асинхронный вызов с возвращаемым результатом
V v = a.bar(param);
...
// использование результата асинхронного вызова.
// если v — всё ещё ожидающий фьючерс, происходит автоматическое ожидание результата (Wait-by-necessity)
v.gee(param);

Когда вызывается foo у активного объекта a, метод возвращает управление немедленно (ведь поток не может обращаться напрямую к объектам другой подсистемы). Аналогично, при вызове bar управление возвращается сразу, но результат ещё не вычислен — возвращается объект-фьючерс, выступающий временным контейнером результата. С позиции вызывающего подсистемы между фьючерсом и обычным объектом нет различий.

После возврата из обоих методов вызывающий поток продолжает работу, как если бы вызовы уже были выполнены. Механизм фьючерсов блокирует поток только в момент обращения к результату (gee у v), если результат ещё недоступен. Такая стратегия синхронизации называется «ожидание по необходимости» (wait-by-necessity).

Примечания

  1. Caromel, Denis (1993-09). “Towards a Method of Object-Oriented Concurrent Programming”. Communications of the ACM. 36 (9): 90—102. DOI:10.1145/162685.162711. S2CID 8310500. Проверьте дату в |date= (справка на английском)
  2. Baduel, Laurent. Programming, Composing, Deploying for the Grid / Laurent Baduel, Françoise Baude, Denis Caromel … [и др.]. — Sprinter-Verlag, 2006-01. — P. 205–229. — ISBN 978-1-85233-998-2. — doi:10.1007/1-84628-339-6_9.
  3. Caromel, Denis. A Theory of Distributed Objects: asynchrony, mobility, groups, components / Denis Caromel, Ludovic Henrio. — Берлин : Springer, 2005. — ISBN 978-3-540-20866-2.

Литература

Дополнительно по теме