OSGi
Что делать, когда стоит задача разработать по-настоящему модульную архитектуру на языке Java? На помощь приходит OSGi (Open Services Gateway Initiative) – спецификация динамической модульной системы и сервисной платформы для Java-приложений, разрабатываемая OSGi Alliance. Спецификация описывает такую модель разработки ПО, при которой компоненты обладают всеми тремя описанными выше признаками. Основой концепции OSGi являются 2 сущности: наборы (Bundles) и сервисы (Services).
Наборы
При разработке ПО на Java, как правило, используются сторонние библиотеки. В мире Java библиотеки упаковываются в специальные файлы с расширением JAR (Java ARchive). Это обычный ZIP-архив, в котором находятся Java-классы (файлы с расширением .class). При этом использование библиотек может быть сопряжено с определенными сложностями. Как только вы начинаете использовать какую-либо библиотеку в своем приложении, вам становятся доступны все классы в этой библиотеке. Дело в том, что разработчики библиотек не имеют возможности спрятать классы, которые используются для реализации их внутренней логики. Таким образом, если вы используете код, который не предполагался для применения вне библиотеки, вы можете столкнуться с несовместимостью при использовании новой версии библиотеки или нарушить ее корректное функционирование. Еще одной проблемой является так называемый JAR Hell. Многие разработчики сломали об него немало копий. Суть состоит в том, что как только у вас в проекте начинают использоваться разные версии одной и той же библиотеки (как правило, это большие проекты, которые эволюционируют со временем), вы можете столкнуться с ситуацией, когда один и тот же класс имеет разные методы в разных версиях библиотеки. Java же устроена так, что будет использована первая версия библиотеки, которую найдет загрузчик классов. Тем самым, обратившись в коде к какому-либо классу во время выполнения программы, вы получите ошибку, что метод, к которому вы обращаетесь, не существует. Связано это с тем, что на этапе выполнения Java ничего не знает о версии библиотеки, которая должна использоваться в том или ином случае. Стоит отметить, что разработчики OSGi решили не изменять структуру JAR-файлов для обеспечения модульности, а просто добавили в них дополнительную информацию, которая используется средой OSGi. Более того, эта дополнительная информация никак не влияет на использование JAR-файлов в обычных Java-приложениях. Итак, чтобы JAR-файл стал OSGi-набором, в него добавляются данные, которые определяют, какие пакеты данного набора доступны для использования вне его (Export-Package) и какие пакеты других наборов требуются для работы этого набора (Import-Package). При этом возможно задать как версию API, которую набор предоставляет для других наборов, так и версию или диапазон версий API, которые набор требует для своей работы от них же. Все классы набора, которые не находятся в его экспортируемой секции, не доступны вне набора. Таким способом OSGi-набор выполняет требование слабой связности. Сейчас большинство Java-библиотек уже являются OSGi ready, т.е. содержат информацию для возможности выполнения в OSGi-контейнере. Также существует множество инструментов и утилит, которые позволяют создать из обычных JAR-файлов модули для OSGi. Так как зависимости между наборами и интерфейсы, предоставляемые этими наборами, имеют четкие версии, среда выполнения OSGi позволяет легко избегать ситуаций JAR Hell, которые мы описали выше. Таким образом, немного расширив описание обычного JAR-файла и добавив поддержку этого описания в среду выполнения, OSGi-сообщество решило проблему создания модулей в Java. Среда выполнения OSGi позволяет динамически загружать и выгружать новые наборы во время выполнения. Более того, OSGi отслеживает зависимости между наборами и динамически разрешает их.
Сервисы
Итак, имея OSGi-наборы, мы можем разрабатывать модульные приложения, которые взаимодействуют посредством интерфейсов (API). Возникает вопрос: где взять класс, который реализует требуемый интерфейс? Добавить такой класс в API набора – плохое решение, т.к. в рамках модульной архитектуры мы договорились не использовать внутренние классы наборов вне этих наборов. Можно было бы использовать шаблон «фабрика» для реализации интерфейса и добавить его в API набора, но разрабатывать каждый раз новый класс для сокрытия реализации интерфейса тоже кажется не лучшей идеей. OSGi решает задачу поиска реализации интерфейса посредством реестра сервисов. Набор может зарегистрировать реализацию с описывающим её интерфейсом в реестре сервисов. Набор, который использует интерфейс из другого набора, может выполнить поиск в реестре требуемой реализации нужного ему интерфейса. Как правило, наборы регистрируют сервисы при запуске в OSGi-контейнере. Более того, в реестре сервисов могут быть зарегистрированы одни и те же интерфейсы с разными реализациями и дополнительными идентификационными данными. При этом набор, который ищет в реестре требуемый ему сервис, может выбрать наиболее подходящий (например, используя фильтрацию).
Микросервисная архитектура в виртуальной машине Java
Некоторое время назад микросервисная архитектура наделала много шума в ИТ-сообществе. Особенно бурно её стали обсуждать после того, как признанный гуру Мартин Фаулер в соавторстве с Джеймсом Льюисом опубликовал статью «Microservices», в которой подробно рассмотрел преимущества разбиения монолитной архитектуры на набор независимых модулей. Каждый из таких модулей представляет собой отдельное приложение. Каждое приложение разрабатывается и развертывается независимо от других. Взаимодействие же между модулями происходит посредством четко определенных интерфейсов с использованием легковесных протоколов (REST, Protocol Buffers, MQ и т.д.). По факту каждый модуль – это микросервис, который выполняет одну определенную задачу и, как правило, содержит минимальное количество кода. Преимуществами такого подхода к разработке ПО являются: Легкость (при разработке микросервиса выполняется реализация всего лишь одной конкретной части функциональности. При этом нет нужды беспокоиться о других частях программы). Простота замены (если вы решите, что в текущей реализации сервис не справляется со своей задачей, вы легко можете переписать его и заменить на новую версию без какого-либо влияния на другие части программы. Более того, без остановки текущего ПО). Повторное использование (т.к. микросервис выполняет одну маленькую задачу, он может многократно использоваться там, где это необходимо). Как видно из описания выше, разработчики модульных приложений с применением OSGi давно пользуются всеми преимуществами микросервисной архитектуры, но только в рамках виртуальной машины Java. Каждый набор с OSGi, который публикует сервис в реестр, является микросервисом внутри Java Virtual Machine, JVM (в мире OSGi такие микросервисы называются µServices).
Red Hat JBoss Fuse
Мы в Центре программных решений используем все преимущества OSGi при разработке ПО для телеком-операторов, страховых, процессинговых компаний. Для этого мы применяем продукт Red Hat Jboss Fuse, а конкретно его конфигурацию Fuse Fabric. Платформа JBoss Fuse предоставляет гибкую OSGi-среду для выполнения модульного приложения. Сегодня к ПО предъявляются такие требования, как непрерывность работы, возможность легкого горизонтального масштабирования, простота развертывания и наличие средств управления кластером для кластерного ПО. Fuse Fabric решает эти задачи. Технология позволяет развернуть несколько экземпляров Red Hat Jboss Fuse и объединить их в кластер, а также предоставляет централизованный инструмент управления получившейся средой. В рамках Fuse Fabric существуют следующие абстракции: Фичи (features) – совокупность OSGi-наборов, которые реализуют какой-либо функционал. Профили (profiles) – совокупность фич, которые должны выполняться в рамках одного контейнера, и конфигурационные настройки для наборов, которые входят в фичу. Контейнеры (containers) – отдельные JVM-процессы, которые выполняются на конкретном узле кластера Fuse Fabric под управлением контейнера Red Hat JBoss Fuse. Таким образом, любое ПО, которое строится на технологии Fuse Fabric, состоит из OSGi-наборов, которые группируются в фичи и развертываются в рамках какого-либо отдельного JVM-процесса на одном или нескольких узлах кластера в соответствии с заранее заданным профилем. Среда Fuse Fabric содержит множество инструментов, которые позволяют легко управлять полученным кластером. Мы можем создавать профили, на основе профилей создавать контейнеры, создавать/удалять/запускать/останавливать контейнеры на любом хосте, который входит в кластер, подключать новые узлы кластера и т.д. И все эти действия мы можем выполнять online, т.е. без прерывания функционирования остальных узлов кластера. Среда поддерживает возможность хранения нескольких версий профилей, фич, OSGi-наборов. В полученной среде OSGi-наборы могут взаимодействовать не только в рамках одного контейнера, но и вызывать друг друга в рамках разных контейнеров (даже в рамках различных хостов). Эту возможность обеспечивает технология Distributed OSGi. Дополнительно – при минимальных затратах на разработку – каждый сервис, который предоставляет OSGi-набор, можно превратить в REST/web-сервис, который можно вызывать из внешних систем. Таким образом, используя Fuse Fabric, мы можем создавать микросервисную архитектуру, которая поддерживает простоту настройки и развертывания, изолированное выполнение сервисов в рамках своих JVM-процессов (со своими специфичными настройками, например, разными настройками сборщика мусора), при этом сервисы взаимодействуют между собой по сети с использованием легковесного протокола. Т.к. продукт Red Hat JBoss Fuse основан на Open Source стеке технологий Apache ServiceMix, в нашем распоряжении есть такие мощные технологии, как Apache ActiveMQ (реализация спецификации JMS), Apache Camel (реализация шаблонов проектирования корпоративных приложений), Apache CXF (библиотека для разработки REST/web-сервисов), Blueprint (предоставляет возможности внедрения зависимостей), Spring и т.д. Поскольку указанные технологии бесшовно интегрируются между собой в Red Hat JBoss Fuse, это значительно снижает время разработки требуемого функционала. В случае с модульным подходом не обходится и без минусов. Непросто преодолеть сложившийся стереотип, что для разработки сложных корпоративных систем следует использовать J2EE и традиционные серверы приложений. В мире разработки принято разрабатывать монолитные приложения, и отсутствие массовых решений на технологии OSGi (по сравнению с J2EE) вызывает сомнения в её применимости. Нам кажется, что плюсы, которые дает OSGi, могут кардинально упростить как разработку ПО для корпоративного сектора, так и дальнейшее его сопровождение и развитие. Не стоит также забывать, что Java 9 уже идет к поддержке модульности (хотя и не в полном объеме, который уже реализован в OSGi), а плюсами микросервисной архитектуры пользуются такие успешные компании, как Netflix, Amazon, The Guardian и др. К сожалению, в рамках одной статьи невозможно рассказать обо всех преимуществах OSGi. Но одно очевидно – эта технология является хорошей альтернативой созданию традиционных монолитных приложений, которые на данный момент правят бал в корпоративном секторе.