Однажды на Joker мы собрали BOF под названием "Java EE vs Spring". Дуэль была оформлена по всем правилам, сообщество Spring пришло в полном составе... а вот второй дуэлянт предпочел не появиться. Точней, Java EE была представлена одним-единственным оракловским евангелистом с французской фамилией. Даже понятно, почему так произошло. Несмотря на то, что пользователей Java EE куча, это не та аудитория, которая измеряет свой успех в количестве вышедших за последнюю неделю пре-альфа-версий и настроена на серьезных щах обсуждать такие темы.

Получается, у нас есть куча каких-то фреймворков, которые есть, которые на хайпе, но кто их использует и как их увидеть вживую? Quarkus, Micronaut, Helidon, вся эта тема со сборкой через Native Image. Кого ни спроси — у всех или Spring Boot, или Java EE, которая даже еще не Jakarta. И вот, выходит свежий Helidon, а на нем на Хабре даже пока не написали, как будто это шутка какая-то. А ведь там исправили больше двух сотен тикетов на Гитхабе. Держите пруфы:

Те, кто использует Helidon в проде, наверняка точно знают, зачем им это нужно. Что делать остальным? Основная задача хомячка — объяснить детям концепцию смерти. Кажется, точно так же, основная задача Helidon для широких народных масс — посмотреть на самые новые фишки Java и понять, нужно вам это или нет.

Что не так со Spring?

Огромное сообщество и экосистема сделали Spring и Spring Boot выбором по-умолчанию. Напиши код, обмажь магическими аннотациями — и дальше Spring сделает всё по красоте, твое приложение почему-то запустится и будет достаточно хорошо работать.

Эти же преимущества одновременно можно считать проблемами. Огромное сообщество ежедневно рождает новые версии каких-то библиотек, хорошо проверить работоспособность и совместимость между которыми - адский труд. Каждую неделю уходят поезда, направление движения которых изестно только тем, кто тратит на их отслеживание кучу времени (большинство этой ерундой не занимаются). У Spring Boot есть проблемы совместимости даже с Hibernate. А вот тут, кстати, вышла свежая Spring Statemachine 4.0.0-M1 - успехов понять, стоит ли это вообще даже тестировать.

Тонны магии на аннотациях очень сложно отлаживать. Не редки ситуации, когда у разработчика залогирована неделя на то, что он нажимает F8 в отладчике, в тщетных попоытках понять, почему одна какая-то фигня не работает с другой. Просто понять, какие аннотации имеются, и как их использовать совместно — это достойная задачаю. Кроме того, магия аннотаций не всегда самая быстрая штука. Конечно, самая частая проблема с медленным стартом приложений — это накатывание миграций и иниациализация хибернейтов, и на фоне этого ужаса тормоза приложения видны не сразу. Но стоит только копнуть внутрь, как это работает, становится страшно. (Если вы помните, когда-то, когда деревья были выше, Женя Борисов хорошо взлетел на этом с докладами серии Spring-потрошитель).

Как всегда в таких случаях, предлагается сделать ультра-простой фреймворк, который будет лишен ненужной сложности. По той же причине на нем можно будет тестировать все новые ништяки (типа виртуальных тредов и GraalVM Native Image) без угрозы что-нибудь сломать. В данном случае, таким фреймворком является Helidon, а тестирует новые модные игрушки на нем не какой-то нунейм из сообщества, а целая корпорация Oracle. И версия у него 4.0.0, что намекает, что на нем можно попытаться писать в продакшен.

SE vs MP

Если зайти на первую страницу документации, то первое, что привлекает внимание — она делится на два раздела, SE и MP.

Это два варианта поставки. Helidon SE — это облачный микрофреймворк, идеалогия которого в минимизации магии. С целью ультра-производительности, упрощения отладки и тестирования новых ништяков. Helidon MP отвечает за Microprofile.

Код на Helidon SE:

Routing routing = Routing.builder()
    .get("/hello",
        (req, res) -> res.send("Hello World"))
    .build();

WebServer.create(routing)
    .start();

Как видим, Helidon SE вам нужен, если хочется выжать весь перформанс по максимуму, и вы готовы пожертвовать для этого фреймворками для dependency injection и прочей магией.

Код на Helidon MP выглядит более привычно глазу пользователя Spring Boot:

@Path("hello")
public class HelloWorld {
    @GET
    public String hello() {
        return "Hello World";
    }
}

Если вы вдруг не использовали Microprofile, и вероятно, не планируете — пара слов, что это такое. Проект MicroProfile начался в 2016 году, когда Oracle решили окончательно закопать передать в сообщество Java EE. Несколько компаний (включая IBM и RedHat) собрались и вместе запилили некую работающую реализацию подмножества фичей EE, на которых можно было продолжать пилить веб-сервисы.

С тех пор многое произошло - в том числе, и Jakarta, и MicroProfile, отправились развиваться в Eclipse. Как все это будет дальше развиваться — неясно, учитывая что в мире Jakarta больше нет проблемы, с которой они боролись во времена Оракла - тормоза в развитии. Последний блог-пост с виженом в блоге Себастиана Дашнера по этому поводу — за 2019 год, с тех пор прошла вся жизнь. Если кто-то знает актуальное (совсем актуальное) состояние вопроса — пожалуйста, расскажите в комментариях!

Возвращаясь к Helidon, у нас есть маленькая удобная реализация Microprofile. Она всего на десяток мегабайт больше, чем SE версия Helidon, и позволяет писать с использованием привычной парадигмы, с использованием внедрения зависимостей и стандартов типа CDI, JAX-RS, JSON-P, JSON-B, итп.

В свежей версии Helidon 4.0.0 появилась возможность использовать новинки из MicroProfile 6.0. А что это такое, лучше почитать на их официальном сайте: https://microprofile.io.

Переобуваемся в Virtual Threads

На протяжении развития Helidon, разработчики строили его SE реализацию поверх асинхронного API. Рассказывали, как это круто и замечательно, и всё теперь точно будет работать быстро. Но выход Java 21 и виртуальных тредов заставил их резко переобуться.

В секретных лабораториях Оракла некоторое время назад начал вариться веб-сервер Nima, который изначально создан для работы с виртуальными тредами. Только с ними — без них он просто не заработает. И вот, в Helidon 4.0.0, Netty с асинхронщиной были выброшены на свалку истории и заменены на эту свежую реализацию. Теперь всё это назвается просто Helidon Web Server, слово Nima в суе не используется.

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

Каждый новый запрос обслуживается в отдельном виртуальном треде. Это позволяет выдерживать нагрузку, кратно большую, чем предыдущие реализации. Так как внутри Helidon MP основан на том же самом SE, всё это привело к интересному эффекту: наибольшую выгоду от использования виртуальных тредов мы получаем именно в MP, что видно на графике.

Если интересно погрузиться в тему, то Митя Александров какое-то время назад написал статью про сравнение Helidon 4 со свежим Spring Boot и вложил тестовый скрипт на GitHub.

Графики внушают уважение!

Время до первого ответа:

Использование оперативной памяти:

Количество бессмысленной траты дисков на зависимости:

Выводы

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

Но даже если вы никогда не планировали использовать Helidon, его успехи - это показатель того, какие можно получить преимущества от использования свежей Java 21, в особенности — виртуальных потоков. Ждем, когда Spring Boot и его интеграцию с Virtual Threads отполируют до такого же зеркального блеска, как это сделано в Helidon!

Кстати, в России у нас есть специальный российский дистриубтив Java: Axiom JDK. Недавно вышла свежая версия Axiom JDK с поддержкой Java 21. Если вы используете Аксиому, попробуйте запустить на ней Helidon и поделитесь результатами!

Подписывайтесь на два моих Telegram-канала:
Javawatch. Анонсы по Java: новости, митапы.
Откровения от Олега. Личный канал + чат практически без цензуры, где можно встретить ребят типа Баруха и всё это обсудить.

Комментарии (7)


  1. tessob
    09.11.2023 05:09
    +5

    Выбор Спринга в большинстве проектов обусловлен развитой экосистемой, кучей обучающих материалов и компактным набором паттернов. Это вообще не про перфоманс ни разу, а про возможность нанять на проект 2-3 сотни одинаково посредственных разработчиков, которые в одинаковой манере пишут код. Просто, бизнес так устроен. Большая часть энтерпрайза — это унылые однотипные crud микросервисы, где 90% серверного времени сервис тупо ждёт сеть.

    Те у кого есть возможность выбирать стек скорее возьмут Rust, чем очередной прорывной жаба-фреймворк.


    1. trix
      09.11.2023 05:09

      скорее антипаттернов. если бы спринг остался на уровне core DI framework, к нему было бы сильно меньше претензий :)) однако он оброс тонной библиотек повторяющих одни и те же плохие подходы и жестко завязанных на spring core, вместо развития творчества людей огромную часть индустрии перегнали из ящика j2ee в ящик спринга.


  1. arkaev
    09.11.2023 05:09
    +2

    Вообще не понимаю, почему вдруг DI стал проблемой, когда есть annotation processors на этапе компиляции. Micronaut тому пример


    1. sergey-gornostaev
      09.11.2023 05:09
      +2

      Проблема не в DI, а в том, что в большинстве java-фреймворков, и в частности в Spring, почти всё делается с помощью рефлексии и прокси-объектов. Микронавт - редкое исключение, но и он опирается на пока не стандартизированный Java Compiler API, что несколько настораживает.


      1. Shatun
        09.11.2023 05:09
        +1

        Проблема не в DI, а в том, что в большинстве java-фреймворков, и в частности в Spring, почти всё делается с помощью рефлексии и прокси-объектов

        Большинство это видимо спринг и ныне живущие больше в виде легаси apllication serverа со своим DI?
        По-моему за пределами спринга большинство популярных DI уже живут в compile time-микровант, кваркус, даггер


        1. Hivemaster
          09.11.2023 05:09
          +2

          Упомянутый в статье microprofile - это CDI с рефлексией. Dagger стоит сравнивать с Guice по частоте и области применения, окажется, что первый применяют в основном в Android, где на рефлексию не хватает ресурсов. Аналогично стоит сравнивать долю рынка кваркуса и микронавта со спрингом, разрыв впечатляет. У Java печально мало работы происходит во время компиляции по сравнению даже с другими языками на той же платформе, не говоря уж о каком-нибудь там Rust.


          1. Shatun
            09.11.2023 05:09
            +1

            Упомянутый в статье microprofile - это CDI с рефлексией

            Как раз для этого был выпущен CDI lite, который легко реализуется без рефлексии.

            Dagger стоит сравнивать с Guice по частоте и области применения, окажется, что первый применяют в основном в Android, где на рефлексию не хватает ресурсов.

            Ну например при использовании в авс лямбде даггер достаточно дефолтный DI на текущий момент. Я его как раз упоменул именно потому что периодически всетрчал в бэке за последние годы.

            Аналогично стоит сравнивать долю рынка кваркуса и микронавта со спрингом, разрыв впечатляет

            Да, спринг к сожалению слишком популярен.

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

            Да пожалуй нет, в джаве в целом нет с этим проблем, выше упомянутые кваркусы и микронавты тому пример. Проблемы у спринга, который очень популярен, но не у джавы в целом.