Pull to refresh
149.6
Инфосистемы Джет
российская ИТ-компания

Модульные приложения на Java. Как?

Reading time 6 min
Views 18K
То, что в Java не поддерживается модульность, известно всем. Проблема существует давно, и было немало попыток исправить ситуацию. Этой цели посвящен проект Jigsaw. В 2011 планировалось добавить поддержку модульности в Java 7, в 2014 – в Java 8, наконец, было анонсировано, что в конце июля 2017 выйдет Java 9 с возможностью модуляризации. Но что же делать в ожидании этого прекрасного мига?

OSGi спешит на помощь


Для создания по-настоящему модульной архитектуры на языке Java можно воспользоваться OSGi (Open Services Gateway Initiative) – спецификацией динамической модульной системы и сервисной платформы для Java-приложений (разработчик OSGi Alliance). Она описывает модель разработки ПО, при которой компоненты обладают тремя основными признаками модульного приложения. Речь об инкапсуляции (каждый модуль скрывает свою реализацию от внешнего окружения), слабой связности (модули взаимодействуют только с помощью заранее оговоренных контрактов) и динамичности (компоненты можно замещать на лету, без остановки всего приложения). Основой концепции OSGi являются 2 сущности: наборы (Bundles) и сервисы (Services).

Bundles


При разработке ПО на 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, которые набор требует для своей работы от них же. Все классы набора, которые не находятся в его экспортируемой секции, не доступны вне набора (Private). Таким способом OSGi-набор выполняет требование слабой связности.

Сегодня большинство Java-библиотек уже OSGi ready, т.е. содержат информацию для возможности выполнения в OSGi-контейнере. Кроме того есть немало инструментов и утилит, с помощью которых можно создать модули для OSGi из обычных JAR-файлов.

Рис. 1.

В OSGi можно легко избегать ситуаций JAR Hell, так как зависимости между наборами и интерфейсы, предоставляемые этими наборами, имеют четкие версии. Можно динамически загружать и выгружать новые наборы во время выполнения. OSGi отслеживает зависимости между наборами и динамически разрешает их.

Services


Итак, с помощью OSGi-наборов мы можем разрабатывать модульные приложения, которые взаимодействуют посредством интерфейсов (API). Но где взять класс, который реализует требуемый интерфейс? Вариант «добавить в API набора» не подходит – в рамках модульной архитектуры мы договорились не использовать внутренние классы наборов вне этих наборов. Другое решение ‒ применить шаблон «фабрика», реализуя интерфейс, и добавить его в API набора. Но и оно не слишком удачно, т.к. для сокрытия реализации интерфейса придется каждый раз разрабатывать новый класс.

Поиск реализации интерфейса в OSGi осуществляется с помощью реестра сервисов. В этом реестре набор может зарегистрировать реализацию с описывающим её интерфейсом. Набор, использующий интерфейс из другого набора, может найти в реестре требуемую реализацию нужного ему интерфейса. Как правило, наборы регистрируют сервисы при запуске в OSGi-контейнере. Плюс ко всему в реестре сервисов могут быть зарегистрированы одни и те же интерфейсы с разными реализациями и дополнительными идентификационными данными. Используя фильтрацию, набор может выбрать в реестре наиболее подходящий из представленных сервисов.

Рис. 2.

Микросервисы Java Virtual Machine


Микросервисная архитектура представляет собой набор независимых модулей – отдельных приложений. Взаимодействие между ними происходит посредством четко определенных интерфейсов с применением легковесных протоколов (REST, Protocol Buffers, MQ и т.д.). По факту каждый модуль – это микросервис, выполняющий одну конкретную задачу и содержащий, как правило, минимальное количество кода. Преимущества этого подхода к разработке ПО:

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

Разработчики модульных приложений с применением OSGi давно пользуются всеми этими преимуществами, но только в рамках виртуальной машины Java. Каждый набор с OSGi, который публикует сервис в реестр, является микросервисом внутри Java Virtual Machine, JVM (в мире OSGi такие микросервисы называются µServices).

Red Hat JBoss Fuse


Мы в «Джете» используем все преимущества OSGi при разработке ПО для телеком-операторов, страховых, процессинговых компаний. Для этого мы применяем Red Hat JBoss Fuse (конфигурация Fuse Fabric). Эта платформа предоставляет гибкую OSGi-среду для выполнения модульного приложения.

Непрерывность работы приложения, легкое горизонтальное масштабирование, простота развертывания, наличие средств управления кластером для кластерного ПО – все эти возможности доступны в Fuse Fabric. Технология позволяет развернуть несколько экземпляров Red Hat JBoss Fuse и объединить их в кластер, а также предоставляет централизованный инструмент управления получившейся средой.

В рамках Fuse Fabric существуют следующие абстракции:

Фичи (features) – совокупность OSGi-наборов, которые реализуют какой-либо функционал.
Профили (profiles) – совокупность фич, которые должны выполняться в рамках одного контейнера, и конфигурационные настройки для наборов, которые входят в фичу.
Контейнеры (containers) – отдельные JVM-процессы, которые выполняются на конкретном узле кластера Fuse Fabric под управлением контейнера Red Hat JBoss Fuse.

Рис. 3.

Любое ПО на базе технологии Fuse Fabric состоит из OSGi-наборов, которые группируются в фичи и развертываются в рамках какого-либо отдельного JVM-процесса на одном или нескольких узлах кластера в соответствии с заранее заданным профилем.

Среда Fuse Fabric дает множество инструментов для легкого управления полученным кластером. Можно создавать профили (а на их основе ‒ контейнеры), создавать/удалять/запускать/останавливать контейнеры на любом хосте, входящем в кластер, подключать новые узлы кластера и т.д. И все это online – без прерывания функционирования остальных узлов кластера. Есть возможность хранить несколько версий профилей, фич, OSGi-наборов.

Благодаря технологии Distributed OSGi, в полученной среде 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, это значительно снижает время разработки требуемого функционала.

Материал подготовлен экспертами Центра программных решений компании «Инфосистемы Джет».
Tags:
Hubs:
+1
Comments 43
Comments Comments 43

Articles

Information

Website
jet.su
Registered
Founded
1991
Employees
1,001–5,000 employees
Location
Россия