В эпоху контейнеров (« эпоха докеров ») Java все еще живет, борется за это или нет. Java всегда была (не) известна своей производительностью, в основном из-за уровней абстракции между кодом и реальной машиной, стоимости многоплатформенности (однажды напиши, запусти где-нибудь — помнишь это?), С JVM в -в промежутке (JVM: программный компьютер, который имитирует действия реальной машины).
Вам также может понравиться:
Все о Spring Boot [Учебники и статьи]
В настоящее время, с микросервисной архитектурой, возможно, это больше не имеет никакого смысла и не дает никаких преимуществ, создавая что-то мультиплатформенное (интерпретируемое) для чего-то, что всегда будет работать в одном месте и на одной платформе ( Docker Container — среда Linux ). Переносимость теперь менее актуальна (возможно, более чем когда-либо), эти дополнительные уровни абстракции не важны.
Сказав это, давайте выполним простое и грубое сравнение между двумя альтернативами для генерации микросервисов в Java : очень известным Spring Boot и не очень хорошо известным (пока) Quarkus .
Соперники
Кто такой Кваркус ?
Набор технологий с открытым исходным кодом, адаптированный к GraalVM и HotSpot для написания Java-приложений . Он предлагает (обещает) сверхбыстрое время запуска и меньший объем памяти. Это делает его идеальным для контейнеров и серверных рабочих нагрузок. Он использует Eclipse Microprofile ( JAX-RS, CDI, JSON-P ), подмножество Java EE для построения микросервисов.
GraalVM является универсальной и полиглотной виртуальной машиной ( JavaScript, Python, Ruby, R, Java, Scala, Kotlin). GraalVM (в частности, Substrate VM ) делает возможным предварительную компиляцию (AOT), преобразуя байт-код в машинный код, в результате чего получается двоичный файл, который может выполняться естественным образом .
Имейте в виду, что не каждая функция доступна в собственном исполнении, компиляция AOT имеет свои ограничения. Обратите внимание на это предложение (цитируя команду GraalVM):
Мы проводим агрессивный статический анализ, который требует предположения о замкнутом мире, что означает, что все классы и все байтовые коды, доступные во время выполнения, должны быть известны во время сборки.
Так, например, Reflection и Java Native Interface (JNI) не будут работать, по крайней мере, из коробки (требует дополнительной работы). Вы можете найти список ограничений здесь, в документе Native Image Java Limitations .
Кто такой весенний ботинок ?
В самом деле? Ну, просто сказать что-то (не стесняйтесь пропустить это), одним предложением: Spring Boot, построенный на основе Spring Framework, является средой с открытым исходным кодом, которая предлагает гораздо более простой способ создания, настройки и запуска веб-приложений Java. , Делая это хорошим кандидатом на микросервисы.
Подготовка к битве — Создание образов Docker
Quarkus Image
Давайте создадим приложение Quarkus, чтобы обернуть его позже в образ Docker. По сути, мы будем делать то же самое, что и руководство по началу работы с Quarkus .
Создание проекта с использованием архетипа Quarkus Maven:
Dockerfile
1
mvn io.quarkus:quarkus-maven-plugin:1.0.0.CR2:create \
2
-DprojectGroupId=ujr.combat.quarkus \
3
-DprojectArtifactId=quarkus-echo \
4
-DclassName="ujr.combat.quarkus.EchoResource" \
5
-Dpath="/echo"
Это приведет к структуре нашего проекта, например:
Обратите внимание, что также были созданы два примера Dockerfiles (src / main / docker): один для обычного образа приложения JVM, а другой для собственного образа приложения .
В сгенерированном коде нам нужно изменить только одну вещь, добавить зависимость ниже, потому что мы хотим генерировать контент JSON.
Dockerfile
xxxxxxxxxx
1
<dependency>
2
<groupId>io.quarkus</groupId>
3
<artifactId>quarkus-resteasy-jsonb</artifactId>
4
</dependency>
Quarkus использует спецификацию JAX-RS в ходе реализации проекта RESTEasy.
Это все, с помощью следующей команды мы видим, что приложение работает:
Dockerfile
xxxxxxxxxx
1
mvn clean compile quarkus:dev
В этом режиме мы также включаем горячее развертывание с фоновой компиляцией. Давайте сделаем простой тест, чтобы увидеть это:
Dockerfile
xxxxxxxxxx
1
curl -sw "\n\n" http://localhost:8080/echo/ualter | jq .
Теперь, когда мы увидели, что он работает, давайте создадим Docker Image. Загрузите GraalVM здесь: https://github.com/graalvm/graalvm-ce-builds/releases .
Важный! Не загружайте последнюю версию 19.3.0 , Quarkus 1.0 не совместим с ней, возможно, Quarkus 1.1 будет. Прямо сейчас версия, которая должна работать, это GraalVM 19.2.1, получите эту.
Сконфигурируйте его домашнюю переменную среды:
Dockerfile
xxxxxxxxxx
1
## At macOS will be: export
2
GRAALVM_HOME=/Users/ualter/Developer/quarkus/graalvm-ce-java8-19.2.1/Contents/Home/
Затем установите собственный образ GraalVM в вашей среде:
Dockerfile
xxxxxxxxxx
1
$GRAALVM_HOME/bin/gu install native-image
Давайте сгенерируем собственную версию для текущей платформы (в этом случае будет сгенерирован собственный исполняемый файл для macOS).
Dockerfile
xxxxxxxxxx
1
mvn package -Pnative
Если все работает нормально, мы можем найти файл с именем quarkus-echo-1.0-SNAPSHOT-runner
внутри папки ./target. Это исполняемый двоичный код вашего приложения, и вы можете просто запустить его выполнив команду: ./target/quarkus-echo-1.0-SNAPSHOT-runner
. Нет необходимости использовать JVM ( обычное приложение: java -cp: lib / *: etc App.jar ), это собственный исполняемый двоичный файл.
Давайте сгенерируем Native Docker Image для нашего приложения. Эта команда создаст собственный образ, то есть образ Docker с собственным исполняемым приложением Linux. По умолчанию собственный исполняемый файл создается на основе текущей платформы (macOS), так как мы знаем, что этот полученный исполняемый файл не является той же платформой, которая будет контейнером (Linux), мы дадим команду сборке Maven генерировать исполняемый файл из внутри контейнера, генерируя собственный образ докера:
Dockerfile
xxxxxxxxxx
1
mvn package -Pnative -Dquarkus.native.container-build=true
На этом этапе убедитесь, что у вас есть среда выполнения контейнера Docker, рабочая среда.
Файл будет 64-битным исполняемым файлом Linux, поэтому, естественно, этот двоичный файл не будет работать на нашей macOS, он был создан для нашего образа контейнера докера. Итак, двигаясь вперед … давайте перейдем к созданию образа докера …
Dockerfile
x
1
docker build -t ujr/quarkus-echo -f src/main/docker/Dockerfile.native .
2
## Testing it...
3
docker run -i --name quarkus-echo --rm -p 8081:8081 ujr/quarkus-echo
Примечание о размере изображения Docker:
Конечное изображение докера было 115 МБ , но вы можете иметь крошечное изображение Docker, используя версию с бездисковым изображением . Дистрибутивные образы содержат только ваше приложение и его зависимости во время выполнения, все остальное (менеджеры пакетов, оболочки или обычные программы, обычно встречающиеся в стандартном дистрибутиве Linux) удаляется. Размер нашего приложения в формате Distroless составляет 42,3 МБ . Файл ./src/main/docker/Dockerfile.native-distroless
имеет квитанцию, чтобы произвести это.
О Distroless Images: « Ограничение того, что находится в вашем рабочем контейнере, на том, что необходимо для вашего приложения, является наилучшей практикой, используемой Google и другими техническими гигантами, которые много лет используют контейнеры в производстве »
Spring Boot Image
На данный момент, наверное, все знают, как создать обычное изображение Spring Boot Docker, давайте пропустим детали, верно? Только одно важное замечание, код точно такой же. Проще говоря, почти то же самое, потому что мы используем аннотации Spring Framework, конечно. Это единственная разница. Вы можете проверить каждую деталь в предоставленном исходном коде (ссылка внизу).
Dockerfile
xxxxxxxxxx
1
mvn install dockerfile:build
2
## Testing it...
3
docker run --name springboot-echo --rm -p 8082:8082 ujr/springboot-echo
Битва
Давайте запустим оба контейнера, запустим их несколько раз и сравним время запуска и объем памяти .
В этом процессе каждый из контейнеров был создан и уничтожен 10 раз. Позже, это было проанализировано их время начала и его след памяти. Числа, показанные ниже, являются усредненными результатами, основанными на всех этих тестах.
Время запуска
Очевидно, что этот аспект может играть важную роль, когда он связан с масштабируемостью и архитектурой без сервера .
Что касается безсерверной архитектуры, то в этой модели обычно эфемерный контейнер запускается событием для выполнения задачи / функции. В облачных средах цена обычно основывается на количестве выполнений, а не на некоторой ранее купленной вычислительной мощности. Таким образом, здесь холодный запуск может повлиять на этот тип решения, так как контейнер (обычно) будет жив только на время выполнения своей задачи.
В масштабируемости ясно, что если необходимо внезапное масштабирование, время запуска будет определять, сколько времени потребуется, чтобы ваши контейнеры были полностью готовы (запущены и работают), чтобы ответить на представленный сценарий загрузки.
Насколько более внезапным является сценарий (необходимый и быстрый), тем хуже может быть случай длительных холодных запусков .
Давайте посмотрим, как они показали время запуска:
Ну, вы, возможно, заметили, что это еще одна проверенная опция, вставленная в график времени запуска. Фактически, это точно такое же приложение Quarkus, но созданное с помощью образа Docker JVM ( с использованием Dockerfile.jvm ). Как мы видим, даже приложение, которое использует Docker Image с приложением JVM Quarkus, быстрее запускается, чем Spring Boot.
Излишне говорить, и, очевидно, победитель, приложение Quarkus Native — это самый быстрый из всех, кто его запустил.
След памяти
Теперь давайте проверим, как обстоят дела с памятью. Проверка того, сколько памяти требуется каждому приложению-контейнеру при его запуске, чтобы он начал работать и был готов к приему запросов.
Заключение
Чтобы подвести итог всего лишь в одном видении, вот что мы увидели в результатах Linux Ubuntu:
Похоже, что Quarkus выиграл эти два раунда боя (Startup Time и Memory Footprint), победив своего противника SpringBoot с некоторым явным преимуществом.
Это может заставить нас задуматься … возможно, пришло время подумать о некоторых реальных лабораториях, опытах и некоторых примерках с Quarkus. Мы должны увидеть, как он работает в реальной жизни, как он вписывается в наш бизнес-сценарий и для чего он будет наиболее полезен.
Но давайте не будем забывать о минусах, как мы видели выше, некоторые функции JVM не могли (пока / легко) работать в собственных исполняемых файлах. В любом случае, возможно, пришло время дать Quarkus шанс проявить себя, особенно если проблема Cold Start беспокоит вас. Как насчет того, чтобы заставить работать один или два модуля Pod (K8) с Quarkus в среде, было бы интересно посмотреть, как он работает через некоторое время, не так ли?
Исходный код от GitHub .
Дальнейшее чтение
Spring Boot: наиболее заметные особенности, которые вы должны знать