Изучение Micronaut и Quarkus против Spring Boot - насколько они хороши?

По состоянию на 2020 год Java по-прежнему остается одним из самых популярных языков программирования для создания веб-приложений, хотя ему приходится сталкиваться с жесткой конкуренцией со стороны новых языков, таких как Go, Python и TypeScript.

В мире Java Spring Framework стала де-факто стандартом для разработки микросервисов. Благодаря таким библиотекам, как Spring Boot и Spring Data, фреймворк прост в использовании и позволяет эффективно и, по большей части, безболезненно разработка.

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

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

Что касается управления ресурсами, Spring (и большая часть платформы Java на самом деле) никогда не пользовалась лучшей репутацией, особенно когда речь идет о накладных расходах, необходимых для одного процесса. Во времена серверов приложений это не было серьезной проблемой, поскольку количество экземпляров было небольшим. Однако с ростом количества микросервисных архитектур и их огромным количеством небольших экземпляров это становится все более и более серьезной проблемой - или, как недавно заявил Кристиан Лусарди:

«Я обнаружил, что базовое Java-приложение, работающее поверх Spring Boot, требует минимум 1 ГБ ОЗУ для работы, и это нормально, когда вы разрабатываете промежуточное приложение, но для архитектуры микросервисов это очень плохо!»

Кандидаты

Весна

Spring возник в 2003 году как ответ на сложность ранней версии Java Enterprise. По своей сути Spring начиналась как среда внедрения зависимостей (DI) и аспектно-ориентированного программирования (AOP), а затем превратилась в простую в использовании структуру веб-приложений. Благодаря обширной документации, широкому использованию и бесчисленным библиотекам Spring позволяет разработчикам эффективно создавать и поддерживать приложения и обеспечивает плавную кривую обучения.

Spring выполняет DI во время выполнения, используя отражение. Таким образом, когда приложение Spring запускается, путь к классам сканируется на предмет аннотированных классов. На основе этого создаются и связываются конкретные объекты.

Хотя это очень гибкий и удобный для разработчиков способ, он может замедлить запуск и потреблять много памяти. Кроме того, этот механизм довольно сложно перенести на GraalVM, поскольку он не поддерживает отражение.

Микронавт

Микронавт - это современный фреймворк с полным стеком микросервисов, представленный в 2018 году создателями Grails Framework.

Он предоставляет все инструменты, необходимые для создания полнофункциональных микросервисных приложений. В то же время он нацелен на обеспечение быстрого запуска и уменьшение объема памяти. Эта цель достигается за счет использования процессоров аннотаций Java для выполнения DI, создания аспектно-ориентированных прокси и настройки приложения во время компиляции, а не во время выполнения.

Многие API-интерфейсы Micronaut созданы на основе Spring и Grails. Это сделано специально и помогает быстро привлекать новых разработчиков. Поэтому Micronaut предоставляет такие модули, как Micronaut HTTP, данные, безопасность и соединители для различных других технологий. Однако зрелость этих библиотек все еще отстает от своих собратьев Spring.

Quarkus

Quarkus - это родной для Kubernetes фреймворк Java, представленный Red Hat в 2019 году. Он построен на основе таких стандартов, как MicroProfile, Vert.x, Netty и Hibernate.

Цель Quarkus - сделать Java ведущей платформой в Kubernetes, обеспечив более быстрый запуск, низкое потребление памяти и почти мгновенное масштабирование платформ оркестровки контейнеров. Quarkus достигает этого, используя специальные плагины Maven для выполнения максимально возможной работы во время компиляции, а не во время сборки (в Quarkus это также называется загрузкой во время компиляции).

Quarkus использует в основном существующие стандартные технологии, но он открыт для расширения. Однако, поскольку проект был запущен всего год назад, зрелость и совместимость этих расширений не всегда ясны. Это, вероятно, изменится в будущем по мере роста платформы.

Helidon MicroProfile

Проект MicroProfile стартовал в 2016 году, когда было непонятно, как Oracle будет продолжать работу над Java Enterprise.

Как и его предшественник, JEE, MicroProfile - это спецификация, которая может быть реализована различными поставщиками.

С тех пор было представлено множество таких реализаций, в первую очередь Payara Micro и Helidon MP. Payara - это сервер Jakarte EE, созданный на основе GlassFish, а Payara Micro - его реализация MicroProfile. Helidon - это среда выполнения, запущенная Oracle в 2018 году и предлагающая собственную реализацию спецификации MicroProfile.

Поскольку спецификации MicroProfile являются производными от JEE, они разработаны и хорошо документированы. Однако отсутствуют соединители для современных технологий или замены для таких библиотек, как Spring Data и Spring Security.

Кроме того, будущее MicroProfile неясно, поскольку тем временем началась разработка Jakarta EE (также в рамках Eclipse Foundation). Поэтому представляется вероятным, что эти два проекта будут объединены - или, по крайней мере, будут тесно координироваться - в будущем.

Сравнение фреймворков

Чтобы сравнить упомянутые фреймворки, я реализовал простое приложение, использующее каждую из них. Пример приложения состоит из интерфейса REST для создания, чтения, обновления и удаления объектов и коннектора реляционной базы данных, который сохраняет эти объекты в таблице.

Если фреймворк поддерживает разные способы доступа к базам данных, я попытался реализовать образцы проектов для разных вариантов. Затем я сравнил производительность этих приложений.

Я запустил это приложение, используя образ OpenJDK Docker. Если фреймворк поддерживает создание собственных образов GraalVM, я также сравнил их производительность. Также ознакомьтесь с моей статьей Реактивный доступ к базе данных с помощью R2DBC, Micronaut и GraalVM для получения дополнительной информации о GraalVM. Исходный код всех этих приложений можно найти на GitHub.

Я сравнил производительность этих приложений на трех ключевых этапах:

  • Насколько легко было реализовать пример приложения? Чтобы реализовать фреймворки, мне пришлось проверять документацию, а также искать информацию на таких платформах, как Stack Overflow.
  • Сколько времени нужно на компиляцию приложения? Я измерил время, необходимое для выполнения чистой сборки, включая создание образа Docker. Для GraalVM это включает время для создания образа в машинном коде.
  • Сколько времени нужно, чтобы запустить приложение? Здесь я измерил время, которое проходит от запуска docker up до того, как приложение правильно ответит на первый HTTP-запрос. Кроме того, я сравнил измеренный объем памяти, занимаемый неактивными приложениями сразу после загрузки.
  • Нагрузка: сколько запросов приложение может обработать при просмотре? Я использовал JMeter для выполнения нагрузочного тестирования и протестировал приложение с 25% запросов, выполняющих запись в базу данных, и 75% из них выполняли только чтение базы данных. Затем я снова измерил объем памяти, занимаемый приложением при максимальной производительности.

Я выполнил все тесты на виртуальной машине Google Cloud Platform с четырьмя процессорами Intel Haswell и 15 ГБ памяти под управлением Ubuntu 19.01. Все измерения были повторены несколько раз, чтобы избежать мешающих факторов. Вы можете найти используемый скрипт, а также необработанные данные на GitHub.

Полученные результаты

Легкость развития

Поскольку у меня были только предыдущие знания об использовании Spring Boot, это немного несправедливое сравнение. Однако при проверке документации, доступной информации и примеров Spring, безусловно, является самым простым фреймворком для начала.

Документация Micronaut выполнена хорошо, и у нее есть API, аналогичный Spring и Grail. Поэтому разработчику Spring легко начать с него.

На мой взгляд, у Quarkus немного более крутая кривая обучения, поскольку библиотеки и API менее развиты по сравнению со Spring и Micronaut. Мне особенно не хватало легкого доступа к базе данных.

Но Helidon, на мой взгляд, явно был последним, так как я изрядно боролся, чтобы заставить приложение работать.

Компиляция

Время компиляции при использовании OpenJDK было примерно одинаковым для всех фреймворков и составляло от 6,98 секунды (Spring с использованием JDBC) до 10,7 секунды (Quarkus).

Однако генерация собственных образов GraalVM оказалась довольно трудоемкой и заняла от 231,2 секунды (Micronaut с использованием JDBC) до 351,7 секунды (Micronaut с использованием JPA). Это делает образы в машинном коде практически бесполезными для разработки, так как ждать четыре минуты для компиляции простого приложения - это слишком много.

Запускать

Приложение Spring Boot, использующее Spring Data, загружалось в среднем за 8,16 секунды. Удаление JPA и Spring Data сократило это до 5,8 секунд.

Здесь Micronaut (5,08 секунды при использовании JPA и 3,8 секунды при использовании JDBC) и Quarkus (5,7 секунды) сдержали свое обещание сократить время запуска.

Только Helidon MP оказался даже медленнее Spring - в среднем 8,27 секунды.

Однако настоящий победитель здесь - GraalVM. Время загрузки собственных образов составляло от 1,39 секунды (Quarkus) до 1,46 секунды (Micronaut с использованием JDBC), что значительно быстрее, чем реализации OpenJDK.

Использование памяти сразу после загрузки выглядело примерно так же. Spring выделил 420 МБ памяти (с использованием Spring Data) и 261 МБ (с использованием JDBC).

Micronaut занимал 262 МБ при использовании JPA и 178 МБ при использовании JDBC.

А Quarkus, имеющий 197 МБ, показал себя намного лучше. Helidon MP с 414 МБ был похож на Spring Boot.

Также здесь собственные образы GraalVM значительно превзошли реализацию OpenJDK, используя только от 7 МБ (Quarkus) до 27 МБ (Micronaut с использованием JPA) памяти.

Пиковая производительность

Под нагрузкой Spring Boot работал достаточно хорошо, имея возможность обслуживать 342 (с использованием Spring Data) и 216 (JDBC) запросов в секунду (r / s) и используя 581 МБ (Spring Data) и 484 МБ (JDBC) памяти. Helidon явно был последним, способный обслуживать только 175 об / с при выделении более 1 ГБ памяти.

Другие фреймворки могли обслуживать от 400 об / с (Quarkus, работающий как собственный образ) до 197 об / с (Quarkus в OpenJDK). Различные реализации Micronaut находились где-то посередине, с небольшим преимуществом для JDBC над JPA и собственных образов над OpenJDK.

Что касается использования памяти, Quarkus на OpenJDK показал себя на удивление хорошо, потребляя всего 255 МБ памяти. Это даже меньше, чем то же приложение, работающее как собственный образ - где в среднем требовалось 368 МБ памяти.

Однако «Микронавт» оказался довольно расточительным. Реализация JPA, работающая в OpenJDK, в среднем использовала 880 МБ, что более чем на 50% превышает использование памяти Spring. Однако использование JDBC и собственных образов помогло Micronaut сократить объем памяти до 367,8 МБ.

Выводы

Новые платформы Java Micronaut и Quarkus обещали более быстрое время запуска и меньший объем памяти по сравнению с существующими средами, такими как Spring и MicroProfile.

Они действительно выполняют это обещание, но только в режиме ожидания или при небольшой нагрузке. Здесь они превосходят Spring, особенно в сочетании с собственными образами GraalVM. Однако под нагрузкой они не дают особого преимущества, даже когда работают как собственные образы.

И поскольку Spring по-прежнему предлагает, безусловно, лучший опыт для разработчиков, на мой взгляд, он по-прежнему лучше всего подходит для Java-фреймворка для приложения микросервисов - даже с учетом его низкой производительности при запуске.

Меня удивили огромные затраты на использование Hibernate / JPA / Spring Data. Даже для этого очень простого приложения накладные расходы с точки зрения памяти (но также и r / s) были огромными. Здесь мне особенно понравилось решение Micronaut Data, которое может автоматически генерировать код репозитория без необходимости использования JPA. Это действительно то, что можно добавить и в Spring Data.

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

Ресурсы