Статьи

Рефакторинг монолитных на микросервисы для приложений Java EE

Задумывались ли вы, что нужно для того, чтобы реорганизовать существующее монолитное приложение Java EE в приложение на основе микросервисов?

В этом блоге объясняется, как тривиальный пример корзины покупок был преобразован в приложение на основе микросервисов, и каковы некоторые проблемы, связанные с ним. Полная база кода для приложений на основе монолитных и микросервисов находится по адресу: github.com/arun-gupta/microservices .

Читайте дальше для полной славы!

Ява ЭЭ Монолит

Монолитное приложение Java EE обычно определяется как архив WAR или EAR. Весь функционал для приложения упакован в единый блок. Например, онлайн-корзина покупок может состоять из функций пользователя, каталога и заказа. Все веб-страницы находятся в корне приложения, все соответствующие классы Java находятся в каталоге WEB-INF/classes , ресурсы — в каталоге WEB-INF/classes/META-INF .

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

  • Разделение проблем, возможно с использованием Model-View-Controller
  • Высокая когезия и низкая связь с использованием четко определенных API
  • Не повторяйся (СУХОЙ)
  • Интерфейсы / API и реализации являются отдельными и соответствуют Закону Деметры . Классы не вызывают другие классы напрямую, потому что они находятся в том же архиве
  • Использование Domain Driven Design для объединения объектов, связанных с доменом / компонентом
  • YAGNI или вам это не нужно: не создавайте то, что вам сейчас не нужно

Вот как может выглядеть банальный монолитный архив WAR корзины:

JavaEE-монолитный

Это монолитное приложение имеет:

  • Веб-страницы, такие как файлы .xhtml , для компонента Пользователь, Каталог и Заказ, упакованные в корне архива. Любые ресурсы CSS и JavaScript, которые совместно используются различными веб-страницами, также упакованы с этими страницами.
  • Классы для трех компонентов находятся в отдельных пакетах в каталоге WEB-INF/classes . Любые служебные / общие классы, используемые несколькими классами, также упакованы здесь.
  • Конфигурационные файлы для каждого компонента упакованы в каталог WEB-INF/classes/META-INF . Любые файлы конфигурации для приложения, такие как persistence.xml и load.sql для подключения и заполнения хранилища данных соответственно, также упакованы здесь.

У него есть обычные преимущества хорошо известной архитектуры, дружественного к IDE, простого обмена, упрощенного тестирования, простого развертывания и других. Но есть и недостатки, такие как ограниченная маневренность, препятствия для непрерывной доставки, «застревание» с технологическим стеком, растущий технический долг и другие.

Несмотря на то, что в наши дни микросервисы все в порядке, монолиты неплохие. Даже те, кто не работают на вас, могут не получить больших или немедленных выгод от перехода на микросервисы. Могут помочь другие подходы, такие как просто лучшая разработка программного обеспечения и архитектура. Микросервисы не являются ни бесплатным обедом, ни «серебряной пулей» и требуют значительных инвестиций для достижения успеха, таких как обнаружение служб, репликация служб, мониторинг служб, контейнеры, PaaS, отказоустойчивость и многое другое.

даже не рассматривайте микросервисы, если у вас нет системы, которая слишком сложна для управления как монолит.

Микросервис Премиум

Микросервисная архитектура для Java EE

Хорошо, я слышал обо всем этом, но хотел бы увидеть до / после, то есть как выглядит монолитная кодовая база и как выглядит реорганизованная микросервисная кодовая база.

Во-первых, давайте посмотрим на общую архитектуру:

JavaEE-microservices

Ключевые элементы в этой архитектуре:

  • Приложение должно быть функционально разложено, если компоненты User, Order и Catalog упакованы в отдельные WAR-файлы. Каждый WAR-файл должен иметь соответствующие веб-страницы ( # 15 ), классы и файлы конфигурации, необходимые для этого компонента.
    • Java EE используется для реализации каждого компонента, но нет долгосрочной приверженности стеку, так как различные компоненты взаимодействуют друг с другом с помощью четко определенного API ( # 14 ).
    • Различные классы в этом компоненте принадлежат одному домену, и поэтому код легче писать и поддерживать. Базовый стек также может измениться, возможно, сведя технический долг к минимуму.
  • У каждого архива есть своя база данных, т.е. нет общего доступа к хранилищам данных. Это позволяет каждому микросервису развиваться и выбирать любой тип хранилища данных — реляционный, NoSQL, плоский файл, оперативная память или что-то еще — наиболее подходящий.
  • Каждый компонент будет зарегистрирован в Реестре услуг. Это необходимо, потому что несколько экземпляров без сохранения состояния каждой службы могут быть запущены в данное время, и их точное местоположение конечной точки будет известно только во время выполнения ( # 17 ). Netflix Eureka , Etcd , Zookeeper — некоторые варианты в этом пространстве ( более подробно ).
  • Если компоненты должны общаться друг с другом, что довольно часто, то они будут делать это с помощью предварительно определенного API. REST для синхронного или Pub / Sub для асинхронного взаимодействия — это обычные средства для достижения этой цели. В нашем случае компонент Order обнаруживает службу User и Catalog и общается с ними с помощью REST API.
  • Взаимодействие с клиентом для приложения определяется в другом приложении, в нашем случае — в интерфейсе корзины покупок. Это приложение в основном обнаруживает сервисы из Service Registry и составляет их вместе. В основном это должен быть тупой прокси-сервер, на котором страницы интерфейса различных компонентов вызываются для отображения интерфейса ( # 18 ). Обычный внешний вид может быть достигнут путем предоставления стандартных ресурсов CSS / JavaScript.

Это приложение довольно тривиально, но, по крайней мере, подчеркивает некоторые основные архитектурные различия.

Монолит против Микросервис

Некоторые статистические данные для приложений на основе монолита и микросервисов сравниваются ниже:

Характерная черта Монолит Microservice
Количество архивов 1 5

  • Контракты (JAR, ~ 4 КБ)
  • Заказ (WAR, ~ 7 КБ)
  • Пользователь (WAR, ~ 6 КБ)
  • Каталог (WAR, ~ 8 КБ)
  • Веб-интерфейс (WAR, 27 КБ)
веб-страница 8 8 (см. Ниже)
Конфигурационные файлы 4

  • web.xml
  • template.xhtml
  • persistence.xml
  • load.sql
3 за архив

  • persistence.xml
  • load.sql
  • web.xml
Файлы классов 12 26

  • Сервис регистрации для каждого архива
  • Классы обнаружения услуг
  • Класс приложения для каждого архива
Общий размер архива 24 КБ ~ 52 КБ (всего)

Проблемы и ТОДО

Вот проблемы, с которыми сталкиваются, и TODO, при рефакторинге монолита для приложения на основе микросервисов:

  • Java EE уже обеспечивает функциональную декомпозицию приложения с использованием упаковки EAR. Каждый компонент приложения может быть упакован в виде файла WAR и объединен в файл EAR. Они могут даже поделиться ресурсами таким образом. Теперь это не настоящий путь к микросервисам, но это может быть временным шагом для начала. Однако следует помнить, что компонент @FlowScoped не правильно активирован в EAR ( WFLY-4565 ).
  • Извлеките все файлы шаблонов, используя шаблоны библиотеки ресурсов JSF.
    • Все веб-страницы в настоящее время находятся в модуле everest но вместо этого они должны everest в каждом компоненте ( # 15 ).
    • Шаблон библиотеки ресурсов должен быть развернут в центральном месте, а не упакован с каждым файлом WAR ( # 16 ).
  • Разделение монолитной базы данных на несколько баз данных требует отдельных сценариев persistence.xml и DDL / DML для каждого приложения. Аналогично, сценарии миграции, такие как использование Flyway, должны быть созданы соответствующим образом.
  • Необходимо было создать интерфейс REST для всех компонентов, к которым должен получить доступ другой.
  • Пользовательский интерфейс все еще находится в одном веб-приложении. Вместо этого его следует включить в разложенный WAR ( # 15 ), а затем снова составить в немом прокси. Это пахнет портлетами?
  • Разверните несколько файлов WAR в PaaS ( # 12 )
  • Каждый микросервис должен быть легко развернут в контейнере ( # 6 )

Вот полный список классов для монолитного приложения:

01
02
03
04
05
06
07
08
09
10
11
12
./target/classes/org/javaee7/wildfly/samples/everest/cart/Cart.class
./target/classes/org/javaee7/wildfly/samples/everest/cart/CartItem.class
./target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItem.class
./target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItemBean.class
./target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItemType.class
./target/classes/org/javaee7/wildfly/samples/everest/checkout/Order.class
./target/classes/org/javaee7/wildfly/samples/everest/checkout/OrderBean.class
./target/classes/org/javaee7/wildfly/samples/everest/checkout/OrderItem.class
./target/classes/org/javaee7/wildfly/samples/everest/checkout/Shipping.class
./target/classes/org/javaee7/wildfly/samples/everest/uzer/Uzer.class
./target/classes/org/javaee7/wildfly/samples/everest/uzer/UzerBean.class
./target/classes/org/javaee7/wildfly/samples/everest/uzer/UzerItem.class

Вот полный список классов для приложения на основе микросервисов:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
./catalog/target/classes/org/javaee7/wildfly/samples/everest/catalog/ApplicationConfig.class
./catalog/target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItem.class
./catalog/target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItemREST.class
./catalog/target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItemType.class
./catalog/target/classes/org/javaee7/wildfly/samples/everest/catalog/ServiceRegistration.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/cart/Cart.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/cart/CartItem.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogBean.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/catalog/CatalogItem.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/checkout/Order.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/checkout/OrderBean.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/checkout/OrderItem.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/checkout/Shipping.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/ServiceDiscovery.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/ServiceDiscoveryStatic.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/ServiceDiscoveryURI.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/ServiceDiscoveryZooKeeper.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/uzer/UzerBean.class
./everest/target/classes/org/javaee7/wildfly/samples/everest/uzer/UzerItem.class
./order/target/classes/org/javaee7/wildfly/samples/everest/order/ApplicationConfig.class
./order/target/classes/org/javaee7/wildfly/samples/everest/order/Order.class
./order/target/classes/org/javaee7/wildfly/samples/everest/order/OrderItem.class
./order/target/classes/org/javaee7/wildfly/samples/everest/order/OrderREST.class
./user/target/classes/org/javaee7/wildfly/samples/everest/uzer/ApplicationConfig.class
./user/target/classes/org/javaee7/wildfly/samples/everest/uzer/UserREST.class
./user/target/classes/org/javaee7/wildfly/samples/everest/uzer/Uzer.class

Будущие Темы

Некоторые из будущих тем в этой серии будут:

  • Нужны ли контейнеры для микросервисов?
  • Как развернуть несколько микросервисов с использованием контейнеров?
  • Как можно легко контролировать все эти услуги?
  • A / B Тестирование
  • Непрерывное развертывание с использованием микросервисов и контейнеров

Что еще вы хотели бы увидеть?

Наслаждайтесь!