(java || kotlin) && devOps
363 subscribers
6 photos
1 video
7 files
337 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Есть еще ряд интересных моментов. Я расскажу про них на примере Spring Boot native image.

Для борьбы с тем, что часть кода недостижима если идти от точки входа (метод main), есть два инструмента.
1) специальный tracing агент, который можно подключить к приложению, и он будет в runtime логировать такие скрытые вызовы. https://www.graalvm.org/22.3/reference-manual/native-image/metadata/AutomaticMetadataCollection/
2) далее можно создать т.наз. hints - подсказки AOT компилятору, что включить в native image, из того, что он не нашел сам - https://www.graalvm.org/latest/reference-manual/native-image/metadata/ Собственно, большая доля в адаптации фреймворка типа Spring для native image - подготовка таких hints, https://docs.spring.io/spring-boot/docs/3.2.1/reference/html/native-image.html

А что делать если в момент сборки еще не ясно - нужен native image или нет? Или нужны обе версии? Нет проблем - можно совместить оба режима JIT и AOT и создать артефакт, Spring Boot Executable Jar, с байткодом и всеми необходимыми для native image метаданными. И собрать из него native image позже в DevOps pipeline при необходимости.

Для Spring Boot есть два режима сборки. Основной - Native Image Using Buildpacks, в котором в итоге получается docker образ. Для него нужен только Docker на машине-сборщике. И т.наз. Native Build Tools - нужно устанавливать дистрибутив GraalVM, содержащий эти tools, в итоге получается бинарник для железа, на котором происходит сборка.

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

#jvm #performance #native_image #spring #docker #buildpacks #cloud #startup_time
👍1🔥1
Всем привет!

Нашел хорошую статью о том, как совместить тестирование Spring контроллеров и один из самых известных фреймворков для тестирования REST - Rest Assured. https://www.baeldung.com/spring-mock-mvc-rest-assured

Кстати, в начале статьи есть ссылка на пример использования чистого Spring MVC Test, если кто его не использовал - можете сравнить синтаксис.

Еще статья хороша тем, что четко разделяет модульные и интеграционные тесты. И я бы разделил точно также) Я иногда задаю вопрос о видах тестов на интервью, ответ мне не всегда нравится. Для ленивых, вкратце - интеграционным тест можно считать, если появляется сеть - открывается порт, вызывается другой процесс, внешнее хранилище, пусть даже и в embedded варианте. Хотя справедливости ради - вопрос холиварный, из-за того, что много пограничных случаев.

#unittests #spring #rest #integration_tests #interview_question
Всем привет!

Продолжим серию с тэгом #interview_question

Вот код:

@Service
public class MyService {

public void syncMethod() {
System.out.println("Synchronous method executed.");
asyncMethod();
}

@Async
public void asyncMethod() {
System.out.println("Async method executed.");
}

}

Что с ним не так?

Подсказка: @Async выполняет код в отдельном потоке.
Еще подсказка: магия Spring работает через proxy объекты. Из-за proxy объектов, к слову, магия ломается на финальных классах, в частности в Kotlin, без специальных настроек.
И последняя подсказка: вызов метода того же класса в Java работает через неявное указание this, и этот вызов идет через пул констант класса
https://ru.stackoverflow.com/questions/846457/Пул-констант-в-java

В общем код проблема в том, что asyncMethod будет вызван синхронно, как обычный метод того же класса, proxy код будет проигнорирован. Аналогичная проблема будет и с @Transactional. Проблема называется self execution.

Решений два:
1) простое и поэтому правильное - вызывать метод asyncMethod() из другого класса
2) сделать self injection - внедрить класс сам в себя. Выглядит странно, может сбить с толку, но наверняка может пригодится в отдельных случаях.

P.S. Еще один оффтопик. Код для поста я попросил сгененировать GigaChat и ChatGPT. Первый не справился, второй - справился, но с третьей попытки. И к тому же его пришлось почистить. Указание на название проблемы - self execution - не помогло, у обоих моделей без уточняющих подсказок выдается пример с рекурсивным вызовом асинхронного метода. В общем быстрее написать самому)

#interview_question #spring #java
👍4
Всем привет!


В продолжение вчерашней темы про магию аннотаций Spring - статья про то, чем различаются @Async и @Scheduled под капотом
https://habr.com/ru/articles/771112/
И почему их может иметь смысл использовать вместе.

И та, и другая аннотация приводит к выполнению кода в отдельном потоке. Вопрос только в том, сколько таких потоков?
@Async по умолчанию создает новый поток.
А все @Scheduled - работают в одном потоке.
Как правильно? Правильно управлять потоками явно, через явное указание пула потоков в обоих случаях.

P.S. Вообще управлять явно, не полагаясь на значения по умолчанию, часто является правильной стратегией. Потоки, таймауты, квоты, кодировки, таймзоны, версии библиотек и образов ...

#spring #spring_magic #multithreading
👍2🔥1
Всем привет!

Продолжим рассказ про разные способы ускорения Java. Для начала я бы разделил ускорение в целом на 4 более конкретных направления:
1) ускорение запуска приложения за счет оптимизации\отмены первоначальной загрузки классов
2) ускорение выхода приложения на оптимальную производительность путем оптимизации JIT - Just In Time - компиляции байт-кода в нативный
3) ускорение запуска и в какой-то степени выполнения приложения за счет более легковесного фреймворка, используемого для разработки приложения
4) оптимизация сборщика мусора для достижения нужного баланса между затрачиваемыми ресурсами и паузой в обслуживании клиентских запросов, она же Stop the World

Сегодня поговорим про первое направление. С одной стороны упомянутые ранее и native image, и CRaC тоже ускоряют запуск. Но обе технологии имеют ограничения. native image запрещает reflection и динамическую загрузку классов. Образ, сохраненный с помощью CRaC, может содержать что-то лишнее, и с данной технологией нельзя просто так перезапустить приложение при сбое - т.к. возможно причина сбоя лежит в данных, подгруженные из образа.

Начну издалека.
В Java 5 появилась вот такая фича - https://docs.oracle.com/en/java/javase/21/vm/class-data-sharing.html Class-Data Sharing, сокращенно CDS.
Фича появилась и была забыта. Есть такие фичи, про которые все забывают сразу после релиза новой Java) Еще модульность из Java 9 можно вспомнить.

О чем эта фича? Мы записываем в файл метаданные загруженных классов из classpath. Потом этот файл мапился в память работающей JVM. Зачем? Цели было две:
1) расшаривание классов между несколькими инстансами JVM и т.об. уменьшение потребления RAM
2) ускорение запуска (вот оно!)

Вначале фича работала только с классами Java core. Файл с архивом классов Java core входит в состав JDK, найти его можно по имени classes.jsa. Занимает на диске сравнительно немного - 10-15 Мб. И кстати, CDS в Java включена по умолчанию, используется как раз этот файл.

Позже, в Java 10 https://openjdk.org/jeps/310 появилась возможность дампить и пользовательские классы, эту фичу назвали AppCDS. В Java 13 создание архива было упрощено https://openjdk.org/jeps/350
Пользовательские классы можно добавить в архив предварительно запустив процесс со специальной опцией командной строки -XX:ArchiveClassesAtExit

А если у нас Spring? Ребята в Spring 6.1 обратили внимание на данную опцию и добавили ключ командной строки, позволяющий собрать информацию о динамически загружаемых классах именно для Spring Boot приложения https://docs.spring.io/spring-framework/reference/integration/cds.html
А еще дали рекомендации, как максимально точно собрать информацию о классах и подтвердили, что данная опция ускоряет загрузку на ~30% https://spring.io/blog/2023/12/04/cds-with-spring-framework-6-1 Почему подтвердили - именно такую цель ставили разработчики CDS в JEP 310, упомянутом выше.

Итого - идея в чем-то похожа на Profile-Guided Optimization. Только здесь мы предварительно собираем информацию не об использовании кода, а о загруженных классах. Чем больше информации соберем - тем быстрее будет старт приложения. Минусы - версия JDK, Spring и classpath в целом должны совпадать при тестовом прогоне и использовании в ПРОМе.


#jre #performance #spring_boot #spring #java_start_boost
🔥3
Всем привет!

Ну и еще одна оптимизация времени старта Java приложения. Самые внимательные уже могли ее заметить пройдя по ссылкам из предыдущего поста.

С момента появления Spring Boot упаковка приложения в fat jar - jar содержащий все зависимости и Tomcat в придачу (или другой контейнер сервлетов) - стала неким стандартом.
Но fat jar при исполнении требуется распаковать. А разархивация всегда требовала времени, не зря архиваторы используются как бенчмарки для процессорных тестов.

Соответственно, можно заранее разложить зависимости по отдельным файлам для ускорения старта. Вот как рекомендует это делать Spring https://docs.spring.io/spring-boot/reference/packaging/efficient.html
Судя по данным статьи из вчерашнего поста это даст еще 25% ускорения при старте https://spring.io/blog/2023/12/04/cds-with-spring-framework-6-1

#performance #spring #jvm #java_start_boost
👍3🔥1
Всем привет!

Ну и последний вариант ускорения старта Java приложения. Самый радикальный, наверное. Отказ от Spring.

Надо отметить, что чистый hello world Spring сервис в плане старта не так уж плох, плюс минус 4 секунды. Основные проблемы начинаются с ростом числа зависимостей. И Spring можно тюнить, подробнее про это можно почитать здесь: https://www.baeldung.com/spring-boot-startup-speed Единственный момент, который мне не понравился - я бы не отключал C2 компиляцию - скорость старта может и увеличится, а вот выйти на оптимальную производительность не получится. И еще интересное исследование - https://github.com/dsyer/spring-boot-allocations Авторы выключили в Spring Boot все, за что мы его любим - Dependency Injection и быструю автоконфигурацию, повесили все на единственный classloader и ускорили старт в 5(!) раз. Только зачем нужен такой Spring?)

Но вернемся к отказу от Spring. Писать на голой Java я не предлагаю) Есть две альтернативы - Quarkus и Micronaut. Оба при создании основной целью ставили получить более быстрый и легковесный фреймворк, чем Spring.

Вот сравнительный бенчмарк Quarkus https://habr.com/ru/companies/haulmont/articles/443242/ Ускорение старта простейшего приложения в 5 раз, до 0.75 секунд. Я беру цифры без native image (GraalVM ), т.к. в этом случае и Spring будет "летать". Для интереса я сравнил локально, разница получилась не в 5 раз, а примерно в 2, с 2.5 до 1.2 секунды. За счет чего получилось ускориться можно почитать тут https://dev.to/nutrymaco/how-quarkus-use-build-time-to-start-your-application-faster-50n Если вкратце - Dependency Injection происходит во время достаточно сложного процесса компиляции.

А вот сравнение Micronaut со Spring https://www.baeldung.com/micronaut-vs-spring-boot Разница чуть поменьше, в 2,5 раза, но тоже ничего) Вот тут, авторы объясняют, почему они быстрее Spring - https://guides.micronaut.io/latest/building-a-rest-api-spring-boot-vs-micronaut-data-gradle-java.html И снова - внедрение зависимостей на этапе компиляции, нет рефлексии и создаваемых в runtime прокси.

Почему я назвал этот вариант самым тяжелым - оба фреймворка сильно отличаются от Spring - по используемым аннотациям, по API в целом. Кроме того они не такие зрелые, им порядка 5-6 лет, поэтому там просто меньше функционала.

#performance #spring #quarkus #micronaut #java_start_boost
👍4🔥2
Небольшое дополнение по Java records. Как известно, Java - это огромное количество библиотек и фреймворков и, следовательно, вопросы совместимости.
Так вот. Spring Boot Web записи поддерживает, как в контроллере, так и в http клиентах. Java сериализация естественно, это же часть спецификации Java. Jackson сериализация. Mapstruct. И Spring Data JPA, там где это возможно, учитывая иммутабельность https://www.baeldung.com/spring-jpa-java-records
Что я забыл? Наверное забыл, но 3 года прошло - поддержку должны были уже запилить)

#java #spring
👍2
Всем привет!

Небольшое развитие поста про выбор альтернативы IDEA Ultimate.
В посте особое внимание по понятным причинам было обращено на поддержку Spring.
Так вот, благодаря комментариям выяснилось, что в России пилится 3 плагина для Spring:

1) Amplicode https://amplicode.ru/
2) Sber\Giga - назовем его так, т.к. его еще никто не видел
3) Explyt https://github.com/explyt/spring-plugin/wiki/Videos

Последний я изучил по видосам - выглядит неплохо как плагин чисто для Spring. А Amplicode вырывается вперед благодаря поддержке JPA, Liquibase, MapStruct, Docker....

Конкуренция, однако. Вот что импортозамещение делает) Будем наблюдать.

За наводку на Explyt спасибо @alex_inozemtsev

#idea #spring
👍2