digital-garden/knowledge/dev/Исследование сборки исполняемых файлов.md

77 lines
9.0 KiB
Markdown
Raw Normal View History

2024-06-13 21:01:37 +03:00
---
aliases:
tags:
- зрелость/🌱
date: "[[2023-10-21]]"
zero-link:
2024-09-01 20:49:54 +03:00
- "[[../../garden/ru/meta/zero/00 Java разработка]]"
2024-06-13 21:01:37 +03:00
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)