77 lines
9.0 KiB
Markdown
77 lines
9.0 KiB
Markdown
|
---
|
|||
|
aliases:
|
|||
|
tags:
|
|||
|
- зрелость/🌱
|
|||
|
date: "[[2023-10-21]]"
|
|||
|
zero-link:
|
|||
|
- "[[00 Java разработка]]"
|
|||
|
parents:
|
|||
|
- "[[Quarkus]]"
|
|||
|
linked:
|
|||
|
---
|
|||
|
|
|||
|
В этой статье представлено сравнение сборок сервиса в режиме JVM и в режиме исполняемого файла. В первой части статьи дана общая теория и сравнение с работой в режиме JVM. А во второй части статьи дана инструкция, как собрать свой сервис в образ с исполняемым файлом.
|
|||
|
|
|||
|
## Обзорная теория
|
|||
|
GraalVM позволяет заранее (в AOT-режиме) компилировать программы в нативный исполняемый файл. Это означает, что вы можете компилировать ваш код Java непосредственно в машинно-специфичный код. Получаемая в результате программа не работает на JVM, но использует все необходимые компоненты, в частности, управление памятью, планирование потоков и прочее.
|
|||
|
|
|||
|
Правда, AOT-компиляция серьезно ограничивает динамические возможности Java (загрузка классов во время исполнения, рефлексия, прокси, т.д.). Quarkus использует GraalVM и предоставляет экосистему, поддерживающую AOT-компиляцию во время сборки.
|
|||
|
|
|||
|
![](hrbgkaa4flnejnwq-qiutmznacu.png)
|
|||
|
|
|||
|
### Производительность
|
|||
|
В блоге кваркуса имеется [только одна статья](https://quarkus.io/blog/runtime-performance/), которая посвещена сравнению производительности работы в режиме JVM и в режиме исполняемого файла. Она датирована [2019](2019.md) годом (версия Quarkus 0.18.0), однако пример на GitHub обновлялся в [2021](2021.md) году. При желании [этот пример](https://github.com/johnaohara/quarkusRestCrudDemo) можно актуализировать и [провести новые тесты](https://quarkus.io/guides/performance-measure).
|
|||
|
|
|||
|
> В статье идет сравнение с фреймворком Thorntail. Хотелось бы увидеть сравнение с Spring, но его нет. Однако, нас в статье будет интересовать разницу между сборкой испольняемого файла и работы в режиме JVM
|
|||
|
|
|||
|
![](throughput.png)
|
|||
|
|
|||
|
Из графика видно, что максимальная пропускная способность, измеренная в запросах в секунду (Req/Sec), в режиме нативной сборки в 2 раза меньше, чем в режиме JVM. Но не спешите делать выводы.
|
|||
|
|
|||
|
При большом количестве одновременных пользователей среднее время отклика для Quarkus в режиме JVM составляет 0,91 мс, а в режиме Native среднее время отклика смещается до 2,43 мс в обмен на более эффективное использование RAM.
|
|||
|
|
|||
|
Время запуска
|
|||
|
![](screen.png)
|
|||
|
Из таблицы видно насколько быстрее стартует исполняемый файл, по сравнению с JVM. В 90 раз быстрее.
|
|||
|
|
|||
|
Максимальное использование RAM
|
|||
|
![](c56a3afe-93a6-4c03-b9ac-bb6da1580e2f.png)
|
|||
|
Максимальное потребление RAM в режиме исполняемого файла почти в 3,5 раза меньше, чем в JVM режиме.
|
|||
|
|
|||
|
### Выводы
|
|||
|
Не смотря на то, что сборка в режиме исполняемого файла проигрывает по пропускной способности в 2 раза, мы можем запустить в 3+ раз больше инстансов, чем при работе в режиме JVM, и тем самым получить больше пропускной способности, чем в режиме JVM. Также стоит отметить, что запуск новой реплики занимает миллисекунды.
|
|||
|
|
|||
|
Все эти особенности сборки исполняемого файла позволяют утилизировать ресурсы более эффективно. Например, мы можем оставлять работать одну/две реплики сервиса, когда нагрузка на сервис минимальная, а в случае увеличении нагрузки мы можем быстро добавить новых интсансов.
|
|||
|
|
|||
|
Но стоит помнить, что сборка исполняемых файлов ограничевает в выборе библиотек, которые можно использовать. Если они используют динамические возможности Java, то исполняемый файл попросту не соберется.
|
|||
|
|
|||
|
В общем, для каждого сервиса должен быть индивидуальный подход. Какие-то сервисы имеет смысл собирать в исполняемые файлы, а какие-то оставить на JVM.
|
|||
|
|
|||
|
Также стоит учитывать, что сборка исполняемых файлов - дорогостоящий процесс, требуется минимум 4 процессора и 4 ГБ памяти. И сборка занимает больше времени. Поэтому необходимо достаточное колличество раннеров, чтобы не было длинных очередей на CI/CD.
|
|||
|
|
|||
|
## Тесты в нашем окружении
|
|||
|
Пока не проводились масштабные тесты, но некоторые эффекты видны невооруженным взглядом.
|
|||
|
|
|||
|
Сборка исполняемых файлов была опробована на [сервисе операторов коммуникационного сервиса](https://gitlab.t1-consulting.ru/t1-crm/communication/communication-operator-service). Там же можно изучить все детальнее.
|
|||
|
|
|||
|
Для теста был выбран GraphQL эндпойнт, который просто ходит в БД и отдает данные из БД. Никакой сложной обработки, никаких вызовов других сервисов, кроме поиска сессии пользователя в Redis.
|
|||
|
|
|||
|
Замеры производились незамысловатым образом, через Postman. Но как я уже сказал, разница слишком очевидна. Также напомню, что на один под выделяется 512 мб RAM.
|
|||
|
|
|||
|
Сразу отмечу, что запуск пода занял меньше секунды, против 40-60 секунд.
|
|||
|
|
|||
|
1. Запрос данных в режиме JVM после того, как сервис был поднят в кубере. Занял 10 секунд, потому что многие бины в кваркусе в режиме JVM инициализируются лениво.
|
|||
|
|
|||
|
![](3%201.png)
|
|||
|
2. Повторный запрос данных в режиме JVM. Были запрошены другие данные (другая страница), чтобы избежать использование кэшей, которые возможно делает кваркус. Занял почти 5 секунд. Но эти данные разняться, но в среднем запрос занимает 1-2 секунды.
|
|||
|
|
|||
|
![](1%201.png)
|
|||
|
3. Запрос данных в режиме исполняемого файла сразу после того, как сервис был поднят в кубере. 796 миллисекунд против 10 секунд. Данные были запрошены те же самые, что и в первом тесте.
|
|||
|
|
|||
|
![](5%201.png)
|
|||
|
4. Повторный запрос в режиме исполняемого файла. Занял 132 миллисекунды против 1-2 секнд в режиме JVM.
|
|||
|
|
|||
|
![](4%201.png)
|
|||
|
## Дополнительные материалы
|
|||
|
* [Сборка Quarkus приложения в исполняемый файл](Сборка%20Quarkus%20приложения%20в%20исполняемый%20файл.md)
|
|||
|
* [BUILDING A NATIVE EXECUTABLE](https://quarkus.io/guides/building-native-image)
|