company_banner

«В идеале мы хотели бы вообще заменить Spring на Micronaut» — интервью с Грэмом Роше о перспективах развития Micronaut


    Micronaut ворвался в нашу жизнь внезапно, без стука и без объявления войны. О нем пишут новости и читают доклады, и вряд ли найдется джавист, который ни разу не слышал это слово. Micronaut — это современный фреймворк для JVM для написания модульных микросервисов и serverless-приложений, использующий для этого фичи GraalVM.


    В свою очередь, Grails — это фреймворк, который появился ещё в 2005 году и с тех пор непрерывно улучшается. В 2015 году Grails 3 переехал на SpringBoot, а версия 4.0.0 появилась в июле прошлого года. У Grails сохраняется своя аудитория, для которой он очень хорошо подходит и решает вполне конкретные проблемы.


    Грэм Роше создал оба этих фреймворка. Мы пригласили Грэма в нашу виртуальную студию, чтобы обсудить, зачем было изобретать велосипеды и городить костыли, не закопать ли Grails и Spring Boot, как поддерживать Jakarta EE и не сломаться, что будет в Micronaut 2, стоит ли писать Micronaut поверх Micronaut и когда уже Java наконец помрёт.


    Интервью ведут:


    • Юрий Артамонов из JetBrains — занимается поддержкой Micronaut в IntelliJ IDEA;
    • Владимир Ситников из NetCracker — занимается производительностью в Java;
    • Олег Чирухин из JUG Ru Group — отвечает за развитие конференции JPoint.

    Юрий и Владимир входят в программный комитет конференции JPoint.


    OpenSource и мотивация


    Олег: Мой первый вопрос довольно очевидный: не могли бы вы представиться и рассказать немного о себе и о своей деятельности?


    Грэм: Меня зовут Грэм Роше, я — руководитель проектов Micronaut и Grails. Я уже 12 лет пишу опенсорсный софт и работаю в консалтинговой компании Object Computing (OCI) в Сент-Луисе, именно они спонсировали разработку Micronaut и Grails.


    Олег: Как вам удалось изобрести свой собственный фреймворк? Многие очень хотели бы совершить такой подвиг, но до дела доходит очень редко.


    Грэм: Нужно много времени, денег, и нужно быть максимально вовлеченным в дело. Очень часто люди со временем теряют интерес к своему проекту — необходимо упорство. Моя карьера до Micronaut и Grails началась в отделе Fujitsu, который разрабатывал на Java системы управления онлайн-обучением. У моего начальства был большой интерес к опенсорсу, много работы велось с проектом Apache Cocoon. Сейчас про него мало кто знает, это была платформа XML/XSLT.


    Для меня знакомство с опенсорсом началось с электронного обучения. Я написал на Groovy инструмент для преобразования документов Word в учебный материал нужного формата. До этого у нас было 15 человек, которые занимались тем, что вручную преобразовывали материал из документов Word в формат, с которым могла работать наша система онлайн-курсов. С моей утилитой всю эту работу автоматизировали, что позволило переключить усилия на более разумные вещи. Благодаря этому проекту я познакомился с сообществом Groovy и вообще с опенсорсом. Гийом Лафорж (глава разработки Groovy и комитета JSR-241 — прим. ред.) в какой-то момент спросил, не хочет ли кто-нибудь сделать аналог Ruby on Rails для экосистемы Java. Наша компания уже использовала Java, Spring и Hibernate в системах управления обучением для наших клиентов. Это был очень трудозатратный процесс, поэтому я решил использовать для этой же цели Groovy в надежде, что так окажется эффективней. Так возник Grails.


    С самого начала Grails был частью моей работы в Object Computing, а когда этот проект обрел действительно широкую популярность, то он стал моей основной работой. Это произошло где-то через год после его возникновения. Мы основали опенсорсную организацию GT1, которая впоследствии была приобретена Spring Source (сейчас они называются Pivotal). В Pivotal я работал в течение семи лет. Всё это дало большой опыт работы с различными фреймворками, и мне захотелось попробовать создать новый фреймворк с нуля, опираясь на накопленные знания об ошибках в Spring и Grails. В частности, ошибок, касающихся потребления памяти, времени запуска и прочего. Этим новым фреймворком стал Micronaut. Думаю, мне не удалось бы создать его внутри Pivotal, потому что не получилось бы совмещать его со Spring. Кажется, что этот новый фреймворк многие встретили с большим пониманием, ведь на тот момент была потребность в более эффективном решении.


    Олег: Скажите, где вы берете силы для того, чтобы снова и снова изобретать новые инструменты? Как вам удается годами трудиться над этим?


    Грэм: Нужно уметь опережать всех на несколько ходов и следить за последними изменениями в отрасли. Когда я работал над Grails, отрасль была совсем другой. Это было больше 10 лет тому назад, архитектуры тогда были совсем другими, микросервисов не было и в помине. Нужно приспосабливаться к изменениям в технологиях и придумывать новые решения. Java тоже не стояла на месте, её разработчики сделали очень многое для запуска Java в контейнерах. Отсюда такой острый интерес к проектам в роде Micronaut, GraalVM и тому подобным. Они пытаются оптимизировать Java для работы в новых условиях с новыми нагрузками. Всегда нужно оставаться актуальным и вносить инновации. Если застрять в зоне комфорта, то тебя неизбежно обойдут другие. Опасайтесь комфорта.


    Олег: Есть какой-то секретный способ, который позволил вам сохранять мотивацию все эти годы?


    Грэм: Мотивация зависит от того, насколько вы вовлечены в свой проект, насколько он вас цепляет. От того, любите ли вы свою работу. Про себя я могу точно сказать, что люблю. Кроме того, нужно иметь в виду, что мне очень сильно повезло, ведь у меня была возможность посвящать всё своё время опенсорсным проектам в течение 13 лет. У проектов, над которыми я работаю, отличные сообщества, и множество увлеченных людей. Для меня всегда очень важно было слышать слова благодарности. Например, когда после выступления на JPoint или какой-нибудь другой конференции ко мне подходят разработчики и говорят, насколько сильно Grails или Micronaut поменяли их жизнь и работу их компаний — это важно.


    Что такое Micronaut


    Олег: На случай, если кто-то из наших читателей не знает, что такое Micronaut — не могли бы вы рассказать о нём?


    Грэм: Micronaut — это относительно молодой фреймворк для Java, Kotlin и Groovy. В нём много фич, унаследованных от Spring и Grails. Принципиальное отличие от предшествующих фреймворков в том, что в Micronaut инфраструктура создается во время компиляции. Micronaut подключается к компилятору Java, Kotlin или Groovy и генерирует всё то, что обычный фреймворк вроде Spring генерирует во время работы приложения при помощи рефлексии (например, определения бинов). Благодаря такому подходу полностью пропадает потребность в рефлексии.


    Когда мы только начинали работу с Micronaut, мы сделали множество тестов производительности приложений Java, Spring и Rails, и пришли к выводу, что рефлексия сама по себе приводит к огромному росту потребления памяти. При любом доступе к классу в Java (к конструкторам, полям и прочему), происходит инициализация всех метаданных рефлексии этого класса. Чем больше методов и полей в классе, тем больше потребление памяти, а в реальных приложениях классы всегда большие. Именно поэтому мы поставили себе задачу полностью отказаться от рефлексии, и именно для этого был создан Micronaut.


    Где-то через несколько месяцев после появления Micronaut Oracle объявили о работе над GraalVM. Это очень интересный проект, у которого есть фича Native Image. Она позволяет создать нативный выполняемый файл для приложения, тем самым позволяя экономить на потреблении памяти, времени загрузки и прочем. Оказалось, что эта фича Native Image и Micronaut отлично дополняют друг друга. GraalVM работает значительно лучше, когда не нужно выполнять рефлексию, в противном случае её нужно объявлять, настраивать, и это требует дополнительных ресурсов. Из всех фреймворков Micronaut — один из самых приспособленных к использованию с Native Image. В Micronaut главное — предварительные вычисления. Для меня наиболее интересное в фреймворках — это компиляция и создание инфраструктуры фреймворка. Мы не хотели, чтобы наш фреймворк оказался просто очередным сервером HTTP, для Java их написаны уже сотни, это уже не интересно. Для нас было интересно всё остальное: как эта система ведет себя во время рантайма, сколько памяти она потребляет, какая у неё производительность, размеры стектрейсов и т. п. У традиционных фреймворков с рефлексией и генерацией байткода (будь то cglib, Byte Buddy или нечто подобное) огромные стектрейсы. Мы хотели, чтобы наши были значительно более компактными. Micronaut — это фреймворк для Java наподобие Spring, но без рефлексии, и благодаря этому потребление памяти у него значительно ниже.


    Судьба Spring Boot, Grails и Groovy


    Олег: Значит ли это, что нам больше не нужны Spring Boot, Grails и тому подобное? Является ли Micronaut волшебным решением всех проблем?


    Грэм: Я сильно сомневаюсь, что такие решения существуют. Spring — отличная технология, и он очень хорошо справляется с поставленной ему задачей. Если пользоваться рефлексией во рантайме, тут Spring отлично себя показывает. Но сейчас благодаря Micronaut у пользователей появился выбор. Для проекта на Micronaut нужно очень мало памяти. Наши клиенты говорили, что благодаря низкому потреблению памяти в Micronaut их затраты на Google Cloud сократились вдвое по сравнению с другими фреймворками. Если новая технология более эффективная, почему бы её не использовать?


    Micronaut предоставляет возможность создать целый ряд новых приложений, причём самых разных, будь то приложения для HTTP-серверов, MessageListeners в Kafka, консольные приложения, лямбды в AWS, serverless-приложения и т. п. Для лямбд и serverless-приложений очень важен быстрый запуск и низкое потребление памяти. Это не значит, что всем нужно всё бросить и перейти на Micronaut: многие проекты не получат никакого преимущества от этого перехода, или полученные преимущества им не нужны на практике. А некоторые другие выиграют очень сильно. Важно наличие выбора.


    Владимир: Если сравнивать Micronaut и Grails, можно ли сказать, что в современный Grails можно было бы встроить Micronaut?


    Грэм: Да. В Grails 4 мы добавили Micronaut в качестве родительского контекста приложения для Spring. Мы поменяли много кода внутри Grails для возможности использовать Micronaut. Это позволило существенно сократить время запуска. Это открыло доступ пользователям к некоторым фичам Micronaut, например, клиенту HTTP. В будущем мы планируем ещё больше внедрять Micronaut в Grails. В идеале мы хотели бы вообще заменить Spring на Micronaut, но это нарушило бы работу приложений множества наших пользователей — в таком же положении находится Spring. Несмотря на это, Micronaut продолжит играть большую роль в развитии экосистемы Grails.


    Юрий: Как вы считаете, рационально ли использовать Groovy для написания микросервисов? Ведь это довольно динамический язык.


    Грэм: Groovy — замечательный язык, я уже давно с ним работаю. Он довольно сильно изменился за время своего существования. Недавно вышел Groovy 3, и там много интересных изменений. Я убежден, что есть такие виды приложений, которые отлично получаются на Groovy. Но если для вас приоритетом является экономия памяти, то Groovy — не самый лучший выбор. Это динамический язык, который активно использует рефлексию. Возможно, это стоит иметь ввиду при выборе языка для serverless-приложений и микросервисов. Несмотря на это, Groovy прекрасно себя зарекомендовал, и в правильном контексте он работает отлично.


    Жертвы ради производительности


    Владимир: Чем именно вам пришлось пожертвовать, концентрируясь на производительности и сокращении потребления памяти?


    Грэм: В первую очередь — временем компиляции. Для работы Micronaut ему необходимо подключиться к компилятору. Если у вас огромный монолитный код на миллионы строк, то ему с Micronaut придется туго, да и с любым фреймворком, который вместо выполнения в рантайме заранее пытается всё скомпилировать. Micronaut значительно лучше использовать с модульными приложениями. Если вы всё-таки хотите запустить Micronaut с приложением в несколько миллионов строк кода, то его лучше разбить на несколько частей, так с ним будет удобнее работать. Для Gradle у нас есть поддержка инкрементной обработки аннотаций, это избавляет от необходимости перекомпилировать целиком весь проект. Разница в скорости компиляции получается не такая уж и большая, но о ней следует помнить. Это именно то, чем мы пожертвовали ради сокращения потребления памяти. И не только мы, при любом использовании AOT этим приходится жертвовать. GraalVM уже заработал дурную славу своим временем компиляции.


    Closed-world assumption


    Владимир: Полагается ли Micronaut на предположение о «closed world»?


    Грэм: Нет. Это в GraalVM всё должно быть статическим, доступным на этапе компиляции и так далее. Micronaut можно использовать на обычной JVM. Более того, если это действительно нужно, можно даже воспользоваться рефлексией. Многие так и делают. Мы поддерживаем Hibernate, и он в обязательном порядке использует рефлексию, мы на это никак повлиять не можем, просто так устроен Hibernate. С другой стороны, в Micronaut Data есть поддержка JDBC, и там рефлексия не используется никак, чтение и запись в базу данных происходит без неё. Мы предоставляем альтернативу для тех, кто хочет добиться максимальной оптимизации от своих приложений, не подключая Hibernate или JPA. И действительно, Micronaut Data JDBC пользуется спросом!


    Владимир: Если я разобью приложение на несколько единиц компиляции, например, JAR-файлов со свободным доступам к классам друг друга, сможет ли Micronaut правильно их связать?


    Грэм: Сможет. Правда, если мы ссылаемся на какой-то внешний проект с аннотацией javax.inject, не собранный через Micronaut, мы не можем интерпретировать его и выполнить инъекцию — для работы нужны метаданные Micronaut, а в этом случае они отсутствуют. Они появляются только при компиляции приложения Micronaut. Проблема возникает исходя из сущности этой технологии. Мы сейчас пытаемся найти решение этой проблемы, чтобы импортировать в проект бины из внешних JAR-файлов — по сути, анализируя классы в CLASSPATH. Скорее всего, это можно будет делать в Micronaut 2.


    Баги в Micronaut


    Владимир: У проекта часто бывает один или два бага, которые невозможно исправить, и от которых невозможно избавиться. Например, проблемы Spring с потреблением памяти, скорее всего, будут существовать столько, сколько просуществует сам Spring. Все знают об этих багах, и они не являются критическими, но они есть.


    Грэм: Конечно, ведь Spring основан на вполне определенной архитектуре…


    Владимир: У приложений, работающих с данными часто есть проблемы с неконсистентностью — на это постоянно висят открытые тикеты, которые никак не закрыть. Есть ли подобные баги или вечно открытые проблемы в Micronaut?


    Грэм: Сейчас мне не приходит в голову ничего такого, что невозможно было бы исправить. Есть проблемы, которые нам очень хочется исправить, но пока для этого просто нет ресурсов. Например, мы не можем полагаться на kapt для обработки аннотаций в Kotlin. Было бы здорово написать нативный плагин и выбросить kapt для Kotlin и одновременно с этим реализовать поддержку Scala с помощью их плагинов компилятора AST. Вряд ли у нас скоро дойдут до этого руки, потому что у нас уже есть работающее решение, хоть оно и не идеально. У kapt есть недостатки — AST Java, к которому дает доступ kapt, не дает информации об аргументах методов по умолчанию, в отличие от нативного AST Kotlin. У нас есть проблемы, которые мы могли бы исправить, но на которые просто нет времени. Если же говорить о вещах, которые невозможны принципиально, то у нас никогда не будет скорости компиляции, сравнимой с проектом, который не занимается обработкой аннотаций.


    Зачем изобретать велосипеды?


    Юрий: Может, стоило починить Spring Boot, вместо того, чтобы создавать очередной новый фреймворк? Если его недостатки устранимы, зачем было создавать новый проект?


    Грэм: На самом деле, мы обдумывали этот вариант, когда только начинали работать над проектом. Проблема в том, что вся модель программирования в Spring Boot очень сильно зависит от рефлексии и генерации байткода во время работы приложения. Все подпроекты в экосистеме Spring основаны на этом — и Spring Data, и Spring Batch, и все остальные. У нас есть Micronaut for Spring, и там используется предварительное вычисление для аннотаций Spring. Но такой проект не совместим с модулями Spring. Так что у команды Spring Boot нет простого способа решить эту проблему, не разрушив при этом существующую экосистему.


    Владимир: Вы говорили о проблеме обратной совместимости — это проекты с большой историей, и на них полагается множество уже написанных приложений.


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


    Новое — хорошо забытое старое


    Владимир: Как думаете, можно ли было создать Micronaut на технологиях 5- или 10-летней давности? Сейчас есть относительно зрелый API для обработки аннотаций, есть Gradle и т. п. Возможен ли был бы Micronaut без этих технологий?


    Грэм: Дело в том, что технологии, о которых вы сейчас сказали, не такие уж и новые. Думаю, Micronaut можно было бы создать и 10 лет назад, просто на менее зрелой платформе. Процессоры аннотаций активно используются в экосистеме Android, а это далеко не новая система. Если подумать, то в Android выбор был остановлен на процессорах аннотаций по той же причине, по которой их выбрали в Micronaut: они всячески старались избежать рефлексии и генерации байткода во время рантайма, поскольку на мобильных устройствах это происходит очень медленно и требует много памяти. В общем, серверной Java есть чему поучиться у Android.


    Мы только сейчас начинаем использовать паттерны, которые применялись при создании Android. Многое из того, над чем мы работали, уже реализовано в Android. Google написал фреймворк Dagger, который очень похож на Micronaut. Вчера кто-то сказал, что Micronaut — это Dagger на стероидах. Думаю, это правильное сравнение: Dagger только выполняет инъекцию зависимостей, а Micronaut делает ещё множество других вещей. Мы только сейчас стали учиться применять эти паттерны к серверной Java для улучшения ее производительности. Хочется снова повторить мысль: если у нас есть возможность улучшить эффективность работы приложения, то почему бы ей не воспользоваться? Почему серверное приложение, написанное на Java, обязательно должно требовать гигабайты памяти? Память сейчас относительно дешевая или даже бесплатная, но это не значит, что мы должны обязательно потреблять гигантский объем ресурсов. И над этой проблемой работает не только Micronaut, память пытаются оптимизировать и другие фреймворки.


    Поддержка спецификаций


    Олег: Почему вы не используете спецификации из Jakarta EE, Java EE и т. д.?


    Грэм: Нас часто об этом спрашивают. По моему опыту работы в Pivotal, Spring Source и с фреймворком Grails, никогда нельзя напрямую делать себя зависимым от одной спецификации. В пользу этого принципа говорит тот факт, что javax собираются переименовать в jakartax. Мы предпочитаем обеспечивать поддержку спецификаций поверх Micronaut. У нас есть поддержка валидации бинов (javax.validation) и javax.inject. Недавно мы выпустили Micronaut Serverless, так что теперь у нас есть поддержка serverless-приложений. Мы выпустили Micronaut JAX-RS, который позволяет использовать аннотации JAX-RS. Преимущество подхода в том, что когда произойдет переход с javax на jakartax нам не нужно будет ломать совместимость с существующими приложениями. Всё необходимое изменят в очередном релизе поддержки JAX-RS или другого модуля.


    У Micronaut много удобных фич, зависящих от обработки аннотаций во время компиляции. Micronaut может преобразовать значение одной аннотации в значение любой другой аннотации во время компиляции. Если определить аннотацию javax.validation, её можно связать с внутренним представлением и не привязывать себя к этому API. Поэтому когда происходит переименование пакетов, нам достаточно выполнить связку с другим набором аннотаций. По нашему опыту многие спецификации, поначалу окруженные большой шумихой, затем попросту сходят со сцены. Примеров этого множество, некоторые из них — Open Tracing, Open Telemetry. Первый раз мы увидели MicroProfile когда разработка Micronaut шла в течение уже шести месяцев. Поначалу я боялся, что о нём точно также все забудут. В будущем мы наверное всё-таки добавим поддержку MicroProfile, но она будет сделана поверх самого Micronaut. Мы планируем добавлять поддержку и других спецификаций, но делать это будем именно таким способом.


    Владимир: Рекомендуете ли вы своим пользователям создавать дополнительный уровень поверх Micronaut?


    Грэм: Это решать им. Мы стараемся принимать правильные решения с точки зрения пользователей фреймворка. Я понял идею, и в этом аргументе есть свой резон. Но у нас есть четкая политика релизов, у нас семантическое версионирование. Мы гарантируем не нарушать совместимость для любых приложений, использующих Micronaut 1.x. В Micronaut 2.x могут появиться изменения, которые нарушат совместимость, но они все будут задокументированы. В общем, мы стараемся правильно работать с ожиданиями пользователей, обновлениями и т. д. В целом наш подход такой же, как у Spring: там тоже поддержку спецификаций добавляют отдельно. Мы предоставляем поддержку спецификаций, но не привязываем себя к пространству имен определенного API, чтобы не оказаться в ситуации, когда этот API, от которого мы зависим, вдруг исчез.


    DevOps


    Владимир: Какая у вас политика релизов?


    Грэм: Очень гибкая. Как правило, мы делаем релизы каждые несколько недель. Наши релизы небольшие, обычно мы их делаем, исправив три или четыре проблемы. Одно из преимуществ Micronaut в том, что у него отлично проработана инфраструктура билдов. Наш процесс релизов заключается в том, что мы просто ставим метки на репозитории. Всё остальное автоматизировано: выгрузка в Bintray, синхронизация с Maven Central, публикация документации, обновление веб-сайта, генерация release notes, и тому подобное.


    Тестирование, реакция на проблемы


    Владимир: Конечные пользователи очень часто используют фреймворки такими способами, которые разработчикам и в голову прийти не могли. Коллекционируете ли вы такие случаи? Где вы берете тестовые данные?


    Грэм: Мы получаем много отчетов о проблемах, это важный источник для тестовых сценариев. Но помимо них есть большая экосистема подмодулей Micronaut, они тоже постоянно выявляют проблемы в ядре Micronaut. Кроме того, для каждого коммита мы выполняем большой набор тестов, который проверяет работу Micronaut с GraalVM Native Image и обеспечивает совместимость. Oracle нам в этом плане помогает, и команда GraalVM получает оповещение, когда совместимость ломается — такие случаи бывали неоднократно. У нас выполняется много автоматизированного тестирования на различных кейсах. Вы правы в том, что заранее предугадать все способы, которыми люди будут использовать Micronaut, невозможно. Люди иногда используют фреймворк способами, о которых мы даже не задумывались, и возникают проблемы, решение которых приходится откладывать до следующего релиза. Думаю, далеко не у нас одних возникают такие ситуации. Их приходится решать по мере поступления. В общем, по мере того, как люди используют фреймворк чаще, у нас увеличивается и его тестовое покрытие.


    Скорость запуска и быстродействие Micronaut дает важное преимущество в плане тестирования. Наш набор тестов огромный, в нем тысячи тестов, но при полной загрузке компьютера я могу выполнить весь набор локально за 5 минут. Разница с Grails тут колоссальная. У него тоже есть модульные тесты ядра, там несколько тысяч тестов, а также сотня функциональных тестов. Из-за того, что Grails зависит от Spring, он запускается значительно медленнее, и его набор функциональный тестов выполняется по полчаса. В ядре Micronaut тысячи функциональных тестов, и им всем приходится запускать сервер, отправлять клиентские запросы. Несмотря на это, время выполнения этих тестов на Github Actions составляет где-то 10-12 минут. Это существенно облегчило нам разработку. При выборе фреймворка люди нечасто думают о том, сколько времени займёт выполнение функциональных тестов.


    Владимир: Тестируете ли вы работу различных компиляторов? У вас ведь довольно плотная интеграция с компилятором Java.


    Грэм: Да, в дополнение к функциональным тестам у нас тысячи тестов, использующих API компилятора Java для компиляции байткода, динамической компиляции исходного кода и его тестирования.


    Совместимость с компиляторами


    Владимир: Простые смертные обычно используют Maven или Gradle, и каждые полгода сталкиваются с тем, что Gradle, скажем, не поддерживает Java 14, его не получается запустить. При разработке фреймворка наверняка ведь хочется знать, совместим ли он с новейшей версией Java?


    Грэм: Где-то год тому назад с нами связалась команда OpenJDK и они предложили нам поучаствовать в их программе раннего тестирования OpenJDK. Мы запускали параллельно тесты для OpenJDK 8, 11 и 13, и мы запускали тесты для каждой пре-релизной версии OpenJDK 14. Где-то в интернете есть страница, на которой перечислены все проекты, систематически тестирующие OpenJDK, и Micronaut входит в этот список.


    Команда и интересы


    Юрий: Наверняка это требует больших трудозатрат. Сколько у вас участников проекта? Много ли вам помогает людей из сообщества?


    Грэм: На полной занятости у нас обычно работает где-то трое людей. Кроме того, в зависимости от количества клиентов, количество разработчиков увеличивается и уменьшается. Есть разработчики, которые хотят временно поработать над опенсорсным софтом. В Micronaut участвует значительно больше людей из сообщества, чем в Grails. Я думаю, что одна из причин этого в том, что Micronaut написан в основном на Java, а Grails — на Groovy, и возможно, что для работы с ним многим было трудно преодолеть языковой барьер.


    Один из проектов, с которым нам очень сильно помогли — это Micronaut OpenAPI, который предоставляет поддержку для Swagger. Это очень интересный проект, поскольку он работает не так, как другие, которые поддерживают Swagger. Обычно для интеграции создается компонент, который анализирует работу приложения во время рантайма, а затем при помощи рефлексии создает Swagger definition в рантайме. При таком подходе потребление памяти очень высокое. Представьте, что нужно динамически сформировать огромную документацию в Yaml для большого приложения во время его работы, а для этого необходимо проанализировать веб-контроллеры, методы и прочее. Наш же проект использует TypeElementVisitor API из Micronaut, который позволяет работать с AST независимо от языка, то есть он работает идентичным образом с Kotlin, Ruby и Java. Благодаря этому возможно генерировать спецификации OpenAPI в момент компиляции. Они создаются в статическом файле swagger.yaml, который можно хранить в статических ресурсах. Благодаря этому для Swagger definition не нужно никакой обработки в рантайме.


    Micronaut в реальном мире


    Юрий: Было бы интересно услышать о каких-нибудь примерах крупных приложений ваших клиентов, использующих Micronaut. Какие у них впечатления, с какими проблемами они сталкиваются?


    Грэм: Тут можно рассказать о SmartThings, подразделении Samsung, которое использует Micronaut для работы с интернетом вещей. Это контекст, где очень важно потребление памяти, поскольку ресурсов на таких устройствах значительно меньше, чем на обычном сервере. Здесь преимущества Micronaut оказываются очень кстати.


    Есть и другие примеры, гораздо большего масштаба. Думаю, все слышали о Minecraft — это, наверное, самая большая компьютерная игра в мире. У них очень много микросервисов, и они сейчас переводят значительную их часть на Micronaut. Это позволит им более эффективно использовать ресурсы. Им приходится иметь дело со всеми сложностями крупной архитектуры, основанной на микросервисах. На основе их откликов мы постоянно оптимизируем наш клиент HTTP, чтобы он лучше держал подключения и тому подобное. Их отчеты о проблемах позволили нам пофиксить много багов и усовершенствовать приложение, так что мы рады сотрудничеству.


    Примеров использования Micronaut много. Ещё один такой пример — сеть супермаркетов Target в США. Micronaut позволил им существенно сократить затраты на Google Cloud.


    Интернет вещей


    Олег: Eclipse сейчас планирует создать инфраструктуру IоT на основе своих новомодных Java-проектов. У них будут довольно жесткие ограничения в плане производительности и потребления памяти. Полезен ли в таком контексте Micronaut? Разумно ли его использовать на контроллерах для умных переключателей света у себя дома?


    Грэм: Да, конечно. В Object Computing мы как раз сейчас работаем над проектом IоT. Мы создаем инфраструктуру поверх облака Amazon AWS IоT. Мы используем Eclipse Paho для интеграции MQTT. Micronaut отлично подходит для этой задачи. Наш фреймворк создан не только для веба, он может работать и на Android (вы можете его запустить на своем телефоне), и на устройствах IоT, на Raspberry Pi. Grails и Micronaut существенно отличаются друг от друга. Главное отличие в том, что Grails — веб-фреймворк, ориентированный на CRUD, Micronaut же значительно более универсален.


    Облачная Java


    Юрий: Давайте поговорим о перспективах Java в облачной среде. Какие сейчас есть новые приложения в этой области? Или использовать Java в облаке по-прежнему нереально?


    Грэм: Ну почему же, вполне реально. Все всё время хотят похоронить облачную Java, но почему-то никак не получается. Java, Spring Boot — это огромные проекты, к Micronaut сейчас очень большой интерес, и есть множество крупных организаций, которые используют Java в облачной среде. Я думаю, в ближайшем будущем нас ждет множество интересных инноваций в этой области. У Java есть довольно очевидные недостатки с «холодным запуском» в serverless-приложениях, но это не значит, что на Java нельзя написать serveless платформу. Думаю, компании, занимающиеся облачными проектами с лямбдами, могут начать использовать Java. Пока этого еще никто не делал. Это позволит избавиться от проблемы «холодного запуска», потому что вместо запуска JVM целиком можно иметь уже «прогретые» JVM с запущенными функциями.


    Ну и вообще, облачная Java — это активно развивающаяся сфера. Ведь значительная часть облачной инфраструктуры реализована именно на Java: Kafka, Cassandra, Elasticsearch. Такие проекты продолжают возникать, так что эта область крайне динамичная. Каждый раз, когда возникает ощущение, что какая-то часть инфраструктуры стабилизировалась, появляется какой-то новый проект, который опять всё переворачивает с ног на голову.


    Юрий: Приятно слышать, что Java ещё жива.


    Грэм: Конечно. Все, кто рассуждают об её умирании, просто не знают, о чём говорят.


    Иерархия компиляторов


    Владимир: Micronaut добавляет дополнительный уровень компиляции. Есть GraalVM Native Image, он компилирует Java в нативный образ; затем Micronaut компилирует аннотации в формат, совместимый с GraalVM…


    Грэм: Я бы сказал немного по-другому: Micronaut генерирует байткод, а не формат, совместимый с GraalVM. Этот байткод совместим с любой JVM.


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


    Грэм: Да, верно.


    Владимир: Мы не собираем приложения для передачи их GraalVM Native Image, а собираем приложение с Micronaut в качестве промежуточного компилятора. Как вы думаете, будет ли Micronaut в будущем отдельным уровнем, или он скорее будет фреймворком? Возникнет ли некий сверх-Micronaut с дополнительными аннотациями, который будет компилировать аннотации, совместимые с Micronaut?


    Грэм: Для первой версии Micronaut мы договорились о формате метаданных байткода. Думаю, на основе этого можно было бы изобрести некий стандарт, который позволил бы кому угодно генерировать при компиляции метаданные, которые затем AOT-компилируются в JVM. В Java сейчас появляется много нового в плане AOT. Конечно, все сейчас говорят о GraalVM. Но есть и другие вещи. Например, интересно наблюдать за тем, что происходит в OpenJ9. Там можно выполнять AOT-компиляцию существующего байткода во время работы приложения. Было бы забавно встроить AOT в процесс поставки — вы выгружаете артефакты, а они уже сами AOT-скомпилировались. Когда я запускаю Micronaut с OpenJ9 со включенным AOT, время запуска получается просто смешное, около 280 миллисекунд. Так что, в этой области есть много возможностей для инноваций. Было бы здорово, если бы эти инструменты были более доступными для разработчиков.


    Доклад на JPoint


    Олег: Не могли бы вы в качестве заключения коротко рассказать нам, о чём будет ваш доклад на JPoint?


    Грэм: Если мне не изменяет память, у меня будет сразу несколько докладов! Один из них будет глубоким погружением в Micronaut. Мне сказали, что на JPoint будет очень подкованная в техническом плане аудитория, которой будет особенно интересен подробный разбор внутреннего устройства наших технологий. Я буду рассказывать, как устроен Micronaut и о его особенностях, чтобы разработчики могли сами для себя составить представление и решить, стоит ли тратить на него время.


    Второй доклад будет посвящен Micronaut Data. Это интересный подпроект в Micronaut, в котором мы применили к базам данным наш основной принцип — обработку при компиляции. Он позволяет во время компиляции создавать запросы, которые затем отправляются в базу данных. Это даёт важное преимущество по сравнению, скажем, с Hibernate и подобными ему решениями, поскольку у нас есть созданные при компиляции запросы, которые мы затем просто выполняем. Это позволяет сократить потребление памяти и ресурсов в рантайме. Думаю, это многим будет интересно, поскольку позволит увидеть на практике реальное использование компонентов Micronaut.


    Приобрести билет на летнюю онлайн-конференцию JPoint можно на официальном сайте. Кроме Грэма Роше в этот раз участвуют легенды Java-мира: гуру перформанса Алексей Шипилёв, отец языка Java Джеймс Гослинг и самый известный евангелист Spring — Джош Лонг. Обратите внимание, что кроме обычного набора билетов есть специальный вариант — Full Pass. Он позволяет участвовать не только в JPoint, а во всех восьми летних онлайн-конференциях.
    JUG Ru Group
    Конференции для программистов и сочувствующих. 18+

    Похожие публикации

    Комментарии 25

      +2
      Спасибо за пост, Олег, это чрезвычайно интересная тема! А нельзя ли послушать запись интервью в оригинале? Было бы круто, чтоб не тратить драгоценное время на чтение. Текст довольно большой. Ну если нет, тогда придётся всё-таки читать.
        +3

        Придется читать, сорри.

        –4
        У Micronaut — устаревший подход. Кодо и классогенерация были популярны 10 лет назад.
        Сейчас же большинство современных Java фреймворков генерируют код в рунтайме. Это и удобнее и больше простора для оптимизации.
          +4

          Ага, а ещё замечательно увеличивается время старта сервера. И мешает делать AOT-компиляцию.

            0
            Если писать приложение так, что падение это инцидент, а не статистика, тогда время старта не играет существенной роли.
              +2
              Команды сейчас деплоят чаще, чем сервера падают. А с автоскейлингом в облаке скорость поднятия сервисов становится ещё важнее.
                +1
                Я с трудом понимаю, как можно что-либо куда-то задеплоить, пусть даже на дев, не проверив 100 раз локально.
                И почему лишняя секунда старта так важна — не совсем понятно.
                Допустим разработчик, закрывает таску и деплоит на дев в конце рабочего дня. И что эта секунда даёт?
                  0
                  Представьте, что вам не хватает производительности и ваши сервисы трещат по швам в black friday, тут вам нужно переживать за скорость запуска новых инстансов сервиса. Если вы не видите зачем это всё, то у вас просто нет таких задач.
                    0
                    Если вам реально нужен real-time, то я сомневаюсь, что Java будет оптимальным выбором. Вы скорее всего выберете ОС реального времени такую как QNX, структуры данных с гарантированным временем доступа и язык без сборщика мусора. Но вообще мне кажется ваш пример сильно высосан из пальца.

                    Кроме того, если следовать вашей парадигме, то вам нужно ещё выкинуть Hibernate, аспекты и некоторые другие фреймворки.
                    Вот уж воистину преждевременная оптимизация — корень всех зол.
                      0

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


                      Разница во времени там не в секунде. Spring Boot грузится пару секунд только если вы не включили ничего, требующего его обычных "умных" механик. Подключаете Spring Security — и вся скорость запуска сразу проседает, наворачиваете работу с БД, кастомный AOP — и всё, скорость запуска в полной жопе. Где. собственно, и должна быть — Spring это не про скорость запуска.

                        –1
                        Вы хотите сказать, что включение Spring Security, поднятие соединений с БД и кастомный AOP на других фреймворках по волшебству происходит быстрее?

                        И сколько секунд вам надо на запуск?
                        Если вам не надо «умные» механики, то не включайте их. Зачем из-за это переходить на фреймворк заведомо ущербнее — непонятно.

                        Мне кажется вы ищете проблем не там где они есть. Если ваше приложение постоянно падает, вы не можете запустить несколько копий параллельно и ваше приложение страдает от производительности то это явно не спринг в этом виноват.
                          +1
                          И сколько секунд вам надо на запуск?

                          Чем меньше — тем лучше. Желательно ноль =)


                          Вы хотите сказать, что включение Spring Security, поднятие соединений с БД и кастомный AOP на других фреймворках по волшебству происходит быстрее?

                          Нет, оно не происходит быстрее. Оно просто не происходит. Ты отказываешься от Spring Security, удобных универсальных рефлективных коннекторов до баз данных, и начинаешь мучиться и пердолиться до тех пор, пока не получится что-то приличное, что влезает в целевые SLA


                          Например, в какой-то момент придется отказаться от обычной джавы и перейти на GraalVM. А может быть, придется отказаться от джавы вообще и перейти на C++, Golang и Node.js.


                          Например, можно написать на Java приложение в том же стиле, что на Ноде — у него есть основной fast event loop который стартует мгновенно, а всё остальное запускается в чисто асинхронном стиле. Тормоза будут происходить не на этапе старта, а на этапе выполнения каждого из запросов, особенно до прогрева локальных (по отношению к типу запроса) кэшей. Но при этом старт ивент лупа — это миллисекунды.


                          AOT мире своя система ценностей — "всё что можно вычить на этапе компиляции — должно быть вычислено на этапе компиляции". В C++ даже специальную фичу добавили — constexpr, завидуйте, кусайте локти джависты. Вон Micronaut Data может предвычислять какие-то SQL запросы, конечно. Благодаря этому, скорость выполнения таких действий в рантайме — тот самый ноль.


                          Другое дело, что на Spring Boot так не пишут. У Spring Boot есть вполне определенная система верований и ценностей. Есть понимание, что принято делать и как принято делать.


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

                          Боюсь, мы тут говорим о совершенно разных вещах, отсюда такой рассинхрон. Я говорю о современных способах развертывания — это публичные и гибиридные облака, или приватные облака "как у Google", когда у тебя по полной используются технологии контейнеризации, включая кубернетисы, клауд-функции, и всё такое прочее. Не обязательно "тратить деньги на Амазон" (скорее экономить, но это другой разговор) — можно и у себя в гараже собрать точно такую же систему, опенстеки с кубернетисами в зубы и погали.


                          Если же речь идёт о том, чтобы поднять на Хецнере один-единственный сервак о сорока четырех ядрах и десяти терабайтах оперативки, и потом вдолбенить в него всю инфраструктуру компании, надеясь что сервак случайно не сотрут хостеры (как когда-то делали наши деды) — тут совершенно другой набор трейдоффов.

                            –3
                            Мы ещё про джаву говорим? Я уже писал выше: если вам нужны милисекунды и вы автоматизируете системы управления ядерным рекатором, то используйте QNX, C и пр. Зачем вы пытаетесь натянуть сову на глобус?

                            Как вы выражаетесь «современных» способов развёртывания — миллион и одна штука. Если у вас нет дублирования, то ни один из них вам не поможет.

                            Вы, надеюсь, в курсе, что в кубернетисе можно поднять несколько инстансов из одного образа? И в Амазоне тоже.
                              +4

                              Нужны не просто миллисекунды, а именно миллисекунды на запуск. Если что-то упало, было перемещено, временно поставлено на паузу, отправлено на реконфигурацию и так далее — когда настанет его час, оно должно взлететь и вернуться в строй моментально, как чертик из табакерки. Особенно это актуально для cloud functions, которые только то и делают что перезапускаются.


                              Это вопрос совершенно ортогональный риалтаймовости после выхода на рабочую нагрузку. Например, в условной черной пятнице у вас могут начать виснуть и перезапускаться приложения — и важно, чтобы они вернулись назад как можно скорее. Но при этом сами эти приложения не только не обязаны быть риалтаймовыми (в смысле QNX), а им это даже вредно. Им по профилю нагрузки положены ни разу не риалтаймовые корутины. Включая джавовый Project Loom, а задолго до него был и вполне себе использовался Quasar. Нужно, чтобы вначале напринимать десятки тысячи соединений от клиентов и не упасть, уйти в спячку на долгоиграющих запросах вроде базы данных и файловой системы, и потом уже когда получится — проснуться и вернуть результат.


                              Мы ещё про джаву говорим?

                              Конечно про джаву. Например, есть чудесный фреймворк Vert.x с ивент-лупами, построенный с использованием не менее чудесного Netty — оба event-driven и non-blocking.


                              Его даже можно собрать с помощью GraalVM, и запустить в Docker, так что имидж в памяти займет 5 мегабайт, на диске — 40, и всё это с условно-мгновенной скоростью запуска.


                              В него можно даже встроить Micronaut DI, потому что жить без DI очень неприятно.


                              Видите, это платформа с совершенно другим набором трейдоффов и практик, чем у Spring Boot. Еще более другие, чем у Micronaut.

                                –3
                                У вас очень странные практики.
                                Это определённо ненормально лезть на прод посреди рабочего дня, чтобы чего-то там переконфигурить вручную без тестирования и быстро перезапуститить пока никто не заметит.

                                Нормальная практика — это когда мы деплоим новую версию приложения (например в куберентис), настраиваем, тестируем, а затем постепенно переводим трафик со старой версии на новую.
                                Если же у нас стартует нагрузка ты мы увеличиваем число инстанцев заранее.

                                Если же у меня вдруг начинает виснуть приложение, то мне надо отключить его от лоад балансера, сделать все дампы, собрать логи и пофиксить ошибку, чтобы такого больше не повторялось. А не в панике перезапускать, считая секунды
                                  +1

                                  Перезапускаешь и переконфигурируешь не ты-как-человек, а гипервизор, каким бы он ни был. Условно говоря, в кубере можно сказать restartPolicy: "OnFailure", maxRetries: "100500" и наслаждаться результатом.


                                  Приложение обычно нельзя отключить, даже если оно падает или виснет. Потому что им пользуются люди, и они не оценят. Скажем так, приложение всегда при каких-то условиях падает и виснет, не бывает идеальных приложений, это не повод чтобы отключить всё от балансировщика, разрегистрироваться в интернете и убить себя об стену :)

                                    +1
                                    Люди пользуются не напрямую, а через load балансер.
                                    В кубере можно указать что угодно, но пользователь этого всё равно не заметит, когда есть дублирование. О чём вообще речь??
                                    У вас нет дублирования? Так и скажите: мы не проходили теорию надёжности в университете и про дублирование не слышали.

                                    И в случае failure надо собирать дампы и логи, анализировать и выпускать фиксы, а не заниматься бездумным перезапуском и прочей ерундой. Чтобы собрать memory дамп, к примеру, требуется намного больше чем 10 секунд, я вас уверяю.

                                    Идеальных приложений не бывает, но нормальное приложение падает не чаще чем раз в год (условно), а не всегда. И в этом случае лишняя секунда даунтайма ничего не решает.
                                    Я уж молчу, что при этом рвутся сессии, сбрасываются кэши и случается куча других неприятностей помимо даунтайма.
              +1
              Грамотно написанное спринговое приложение с embeded сервером стартует за пару секунд. А вот насколько увеличивается время компиляции при Micronaut кодогенерации — это отдельный вопрос.
              +2
              Если фреймворк на самом деле экономит существенный объем памяти относительно того же Спринга, тогда ему найдется применение. Упомянутый в статье IoT — хороший пример.
                +2

                Если не относиться к фреймворкам исключительно как объектам моды (почему бы тогда не заняться коллекционированием шляп?), то подход может быть "подходящий" и "неподходящий" ;)


                Есть же не только дихотомия на AOT vs JIT. Например, медленно компилирующее в AOT приложение — так же неудобно разрабатывать, как и жирную муть на Spring Boot которая стартует по минуте. Тебе как разработчику какая разница где ждать — один фиг ждать придется. Надо сюда ещё добавить как минимум ось про скорость фидбека, и еще одну ось про микросервисы против жирносервисов, и еще, и еще… короче, проблема куда больше

                  +1

                  Если микросервис — действительно микро, а не монолит запиленный как микросервис, то разница между двумя "легковесами" вряд ли будет значительна.
                  С другой стороны заказчик на уменьшение счетов за облака обязательно скажет ееее, давай еще. Если крупный — ему экономия, а если мелкий, может вообще захочет на лямбдах AWS — и что тогда делать?
                  И вот, выбирая между хотелками разработчика и хотелками заказчика есть очень ненулевой шанс, что заказчик будет первичен.
                  Вообще, спринг (особенно бут) последних лет нередко монструозен. Это вряд ли будет продолжаться вечно. В разработке — да, удобен, но этот плюс ведь не главное, правда?

                    0
                    История циклична :)
                    0

                    Все бы ничего, но основная задача так и не решена: приложение на Micronaut все-равно стартует долго. Когда все статично слинковано, и в рантайме отсутствует всякая кодогенерация, ожидание, что все запустится очень шустро. Но вот пустые Helidon и Quarkus почему-то стартуют быстрее.

                      +1
                      Это смотря кто и как замеряет :) Вот, например, другие цифры.
                        –2

                        … и ЧТО замеряет — это Micronaut 2.0 M2 на пустом Rest. Берем Javalin/SparkFramework и получаем то же самое, но быстрее. А как начнем наваливать технологии, запуск опять сильно просядет.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое