Введение в Quarkus: почему разработчики Java переходят на сверхзвуковую Java
На протяжении почти трех десятилетий Java была доминирующей силой в разработке корпоративного программного обеспечения. Его богатая экосистема, надежная объектно-ориентированная основа, независимость от платформы благодаря виртуальной машине Java (JVM) и проверенные в боевых условиях платформы, такие как Spring Boot, сделали его бесспорным королем серверной инфраструктуры.
Однако переход к облачной архитектуре, оркестрации Kubernetes, контейнеризации (Docker) и бессерверным вычислениям (AWS Lambda, Knative) выявил серьезную уязвимость в традиционных платформах приложений Java: высокие затраты памяти и медленное время запуска.
Когда микросервису необходимо горизонтально масштабироваться от нуля до 100 реплик в ответ на всплеск трафика или когда бессерверная функция выполняется по требованию, ожидание загрузки процесса Java от 3 до 10 секунд недопустимо. Современная облачная инфраструктура требует мгновенного запуска и небольшого потребления памяти — качества, традиционно присущие таким языкам, как Go, Rust или Node.js.
Встречайте Quarkus: нативную платформу Java для Kubernetes, разработанную специально для GraalVM и OpenJDK HotSpot. Часто называемый «Сверхзвуковой субатомной Java»*, Quarkus фундаментально меняет способ компиляции, загрузки и запуска Java-приложений.
В этом подробном руководстве мы рассмотрим, почему разработчики Java используют Quarkus, как Quarkus обеспечивает время запуска менее секунды и объем микропамяти, архитектурную механику оптимизации времени сборки и как создать готовый к работе реактивный микросервис на Java с помощью Quarkus.
1. Дилемма облачной среды традиционной Java
Чтобы понять, почему существует Quarkus, мы должны изучить, как «под капотом» работают традиционные Java-фреймворки, такие как Spring Boot или Jakarta EE.
A. Стоимость инициализации тяжелой среды выполнения
Традиционные платформы Java в значительной степени полагаются на динамическое отражение во время выполнения, сканирование путей к классам и генерацию прокси:
- Сканирование пути к классам. Во время запуска JVM сканирует каждый файл JAR в пути к классам, чтобы обнаружить такие аннотации, как
@Component,@Service,@Controllerили@Entity. - Обработка и отражение аннотаций. Платформа использует отражение (
Class.forName(),getDeclaredFields()) для построения в памяти графа компонентов, свойств конфигурации и зависимостей. - Динамическое создание прокси: CGLIB или ByteBuddy генерирует динамические прокси-байт-коды в памяти времени выполнения для поддержки аспектно-ориентированного программирования (AOP), управления транзакциями базы данных (
@Transactional) и границ безопасности. - Метапространство и расширение RSS. Все метаданные, отраженные дескрипторы классов и сгенерированные прокси должны храниться в памяти внутри метапространства JVM и размера резидентного набора (RSS).
Этот цикл отражения во время выполнения требует значительных циклов ЦП и увеличивает потребление динамической памяти. Базовая служба CRUD REST в традиционной Java может легко занимать от 140 до 300 МБ ОЗУ в режиме ожидания и запускаться от 3 до 8 секунд.
B. Финансовые штрафы за облачные вычисления
В бессерверных и контейнерных средах поставщики облачных услуг взимают плату на основе двух показателей: выделенной памяти (ГБ) и длительности выполнения (миллисекунды).
- Холодный запуск. Если для загрузки бессерверной функции требуется 5 секунд, конечные пользователи сталкиваются с заметной задержкой, и вы платите за 5 секунд вычислений при запуске в режиме ожидания.
- Плотность и масштабируемость модулей. В кластере Kubernetes с рабочим узлом, имеющим 16 ГБ ОЗУ, вы можете запустить только около 40 традиционных экземпляров микросервиса Spring Boot, прежде чем у вас закончится память. Если бы каждый экземпляр потреблял только 15 МБ ОЗУ, на одном узле можно было бы разместить более 800 экземпляров.
2. Архитектура Quarkus: перенос работы со времени выполнения на время сборки
Quarkus решает дилемму облачных технологий посредством радикального изменения архитектурной парадигмы: перенос динамических операций из среды выполнения во время сборки.
Обработка во время сборки (заранее время оптимизации)
Вместо выполнения сканирования пути к классам, анализа аннотаций и связывания графов компонентов каждый раз при загрузке приложения Quarkus выполняет все эти тяжелые операции один раз на этапе сборки (mvn package или ./gradlew build).
- Архитектура расширений и этапы сборки: Quarkus использует подключаемую платформу расширений. При компиляции вашего приложения расширения Quarkus анализируют аннотации, генерируют оптимизированный статический байт-код и заранее разрешают графы внедрения зависимостей.
- Предварительно подготовленные метаданные: все метаданные внедрения зависимостей предварительно рассчитываются. При запуске приложения Quarkus напрямую создает экземпляры предварительно скомпилированных классов, не вызывая отражения и не сканируя пути к классам.
- Устранение мертвого кода (Tree Shaking). В процессе сборки Quarkus определяет классы, методы и библиотеки, которые не используются вашим приложением, и полностью удаляет их.
К моменту создания JAR-файла вашего приложения или собственного двоичного файла все накладные расходы времени выполнения устраняются. JVM просто загружает предварительно подключенные статические байт-коды и мгновенно запускается.
3. Собственный образ GraalVM и OpenJDK HotSpot
Quarkus предлагает модель двойного выполнения: он работает исключительно быстро в стандартном OpenJDK HotSpot, но достигает максимального потенциала производительности при компиляции в собственный образ GraalVM.
+-----------------------------------------------------------------------+
| Java Source Code (.java) |
+-----------------------------------------------------------------------+
|
v (Standard javac)
+-----------------------------------------------------------------------+
| Bytecode (.class) |
+-----------------------------------------------------------------------+
/ \
/ \
v v
+----------------------------------+ +----------------------------------+
| OpenJDK HotSpot JVM | | GraalVM Native Image (AOT) |
| - JIT Compilation (C1/C2) | | - Substrate VM |
| - Dynamic Class Loading | | - No Classpath Scanning |
| - Fast throughput, longer boot | | - Millisecond boot, tiny RAM |
+----------------------------------+ +----------------------------------+
Предварительная компиляция (AOT) и виртуальная машина подложки
GraalVM Native Image берет байт-код Java и компилирует его непосредственно в автономный исполняемый двоичный файл для конкретной ОС (двоичный файл ELF в Linux, Mach-O в macOS, EXE в Windows).
- Предположение о закрытом мире: GraalVM предполагает, что весь доступный код, классы и ресурсы известны во время сборки.
- Substrate VM: Собственный исполняемый файл включает в себя миниатюрный механизм выполнения под названием Substrate VM, который управляет памятью, планированием потоков и сборкой мусора без запуска полного экземпляра JVM.
- Нулевые затраты на отражение: поскольку Quarkus подготавливает конфигурации отражения и определения прокси во время сборки, компиляция собственного образа GraalVM проходит гладко без файлов ручной настройки, которые исторически требовались для собственных сборок GraalVM.
4. Контрольные показатели производительности: эмпирические данные
Чтобы проиллюстрировать резкий контраст в производительности, давайте рассмотрим сравнение стандартных отраслевых тестов для трех конфигураций среды выполнения Java, использующих стандартную службу REST + Database CRUD:
- Традиционный облачный стек (традиционная JVM/Spring Boot)
- Quarkus в OpenJDK HotSpot
- Quarkus в собственном образе GraalVM
Сводная таблица производительности
| Метрика | Традиционный стек (JVM) | Кваркус (JVM HotSpot) | Кваркус (родной GraalVM) |
|---|---|---|---|
| REST RSS-память | ~140 МБ | ~74 МБ | ~13 МБ |
| REST + RSS-память CRUD | ~218 МБ | ~112 МБ | ~35 МБ |
| Время запуска REST | ~4,3 секунды | ~0,98 секунды | ~0,014 секунды (14 мс) |
| REST + Время запуска CRUD | ~9,5 секунд | ~2,0 секунды | ~0,042 секунды (42 мс) |
| Исполняемый артефакт | Большой толстый JAR (~50 МБ) | Оптимизированный JAR (~20 МБ) | Автономный собственный двоичный файл (~30 МБ) |
Обратите внимание, что Quarkus, скомпилированный в собственный образ, загружается за 14 миллисекунд — быстрее, чем одно мгновение — и потребляет всего 13 МБ ОЗУ. Это делает Java полностью конкурентоспособным с Go и Rust для бессерверных и облачных развертываний.
5. Реактивный и императивный двухъядерный двигатель
Исторически разработчикам Java приходилось выбирать между двумя взаимоисключающими моделями программирования:
- Императивный (потоков на запрос): простой, читаемый код блокировки с использованием стандартных драйверов JDBC и синхронных конечных точек REST.
- Реактивный (цикл событий): асинхронный неблокирующийся код (например, RxJava, Project Reactor), обладающий высокой пропускной способностью, но известный своими сложными цепочками обратных вызовов и сложной отладкой.
Quarkus объединяет оба мира в одном связном движке, работающем на базе Eclipse Vert.x и Netty.
+---------------------------------+
| Client HTTP Request |
+---------------------------------+
|
v
+---------------------------------+
| Eclipse Vert.x I/O |
| (Event Loop) |
+---------------------------------+
/ \
/ \
v v
+-------------------+ +-------------------+
| Reactive Endpoint | |Blocking Endpoint |
| (Event Loop) | | (Worker Thread) |
| - Mutiny (Uni/Multi)| | - Standard JDBC |
| - Non-blocking | | - Imperative Code |
+-------------------+ +-------------------+
В Quarkus основой является неблокирующий ввод-вывод. Если вы пишете традиционный блокирующий код, Quarkus автоматически отправляет выполнение в управляемый пул рабочих потоков. Если вы используете реактивные типы, такие как SmallRye Mutiny (Uni<T> и Multi<T>), выполнение остается в высокопроизводительном неблокирующем потоке цикла событий.
6. Радость разработчика: услуги живого кодирования и разработки
Помимо производительности во время выполнения, Quarkus предоставляет революционные возможности для разработчиков, призванные устранить утомительный цикл обратной связи «сборка-тестирование-перезапуск».
A. Живое кодирование с нулевым перезапуском (quarkus:dev)
При запуске mvn quarkus:dev Quarkus запускает режим живого кодирования. Вы можете редактировать файлы Java, изменять свойства, изменять шаблоны HTML или обновлять схемы базы данных.
В следующий раз, когда вы вызовете HTTP-запрос в браузере или терминале, Quarkus обнаружит изменения в файле, повторно применит этапы сборки и перезагрузит приложение в горячем режиме за менее 500 миллисекунд. Вам никогда не придется вручную останавливать и перезапускать сервер приложений во время разработки.
B. Quarkus Dev Services (тестовые контейнеры с нулевой конфигурацией)
Подключение микросервиса к базе данных PostgreSQL, брокеру Kafka или кешу Redis обычно требует написания файла docker-compose.yml и настройки портов локальной базы данных.
С помощью Quarkus Dev Services:
- Если Quarkus обнаруживает зависимость базы данных (например,
quarkus-reactive-pg-client), но URL-адрес базы данных не настроен вapplication.properties, Quarkus автоматически запускает контейнер Docker, запускающий PostgreSQL через Testcontainers в фоновом режиме. - Он автоматически вводит учетные данные подключения в работающее приложение.
- Когда вы выходите из режима разработки, контейнер полностью очищается.
7. Практическое руководство: создание готового к эксплуатации реактивного сервиса Quarkus
Давайте создадим чистый, высокопроизводительный микросервис Quarkus на Java, который предоставляет REST API, подключенный к базе данных PostgreSQL, с помощью Hibernate Reactive with Panache.
Шаг 1. Зависимости проекта (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ghaznix.quarkus</groupId>
<artifactId>user-service</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<compiler-plugin.version>3.13.0</compiler-plugin.version>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.15.1</quarkus.platform.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- RESTEasy Reactive for high-performance HTTP endpoints -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<!-- Hibernate Reactive with Panache for active-record data access -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive-panache</artifactId>
</dependency>
<!-- Reactive PostgreSQL Driver -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
<!-- Quarkus SmallRye OpenAPI / Swagger UI -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
<!-- Testing -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Шаг 2. Определите реактивную сущность (UserEntity.java)
Quarkus упростил доступ к данным с помощью Panache, реализации шаблона Active-Record поверх Hibernate.
package com.ghaznix.quarkus.entity;
import io.quarkus.hibernate.reactive.panache.PanacheEntity;
import io.smallrye.mutiny.Uni;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import java.time.Instant;
@Entity
@Table(name = "users")
public class UserEntity extends PanacheEntity {
@NotBlank(message = "Username cannot be blank")
@Column(unique = true, nullable = false)
public String username;
@Email(message = "Email must be valid")
@Column(unique = true, nullable = false)
public String email;
@Column(nullable = false)
public String role;
@Column(name = "created_at", nullable = false, updatable = false)
public Instant createdAt = Instant.now();
/**
* Helper method to find a user reactively by email.
*/
public static Uni<UserEntity> findByEmail(String email) {
return find("email", email).firstResult();
}
}
Шаг 3. Реализация реактивного ресурса REST (UserResource.java)
Используя RESTEasy Reactive и SmallRye Mutiny, наши конечные точки HTTP работают полностью асинхронно в неблокирующих потоках:
package com.ghaznix.quarkus.resource;
import com.ghaznix.quarkus.entity.UserEntity;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.validation.Valid;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.net.URI;
import java.util.List;
@Path("/api/v1/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@ApplicationScoped
public class UserResource {
@GET
public Uni<List<UserEntity>> getAllUsers() {
return UserEntity.listAll();
}
@GET
@Path("/{id}")
public Uni<Response> getUserById(@PathParam("id") Long id) {
return UserEntity.<UserEntity>findById(id)
.onItem().ifNotNull().transform(user -> Response.ok(user).build())
.onItem().ifNull().continueWith(() -> Response.status(Response.Status.NOT_FOUND).build());
}
@POST
public Uni<Response> createUser(@Valid UserEntity user) {
return Panache.withTransaction(user::persist)
.replaceWith(() -> Response.created(URI.create("/api/v1/users/" + user.id))
.entity(user)
.build());
}
@DELETE
@Path("/{id}")
public Uni<Response> deleteUser(@PathParam("id") Long id) {
return Panache.withTransaction(() -> UserEntity.deleteById(id))
.map(deleted -> deleted
? Response.noContent().build()
: Response.status(Response.Status.NOT_FOUND).build());
}
}
Шаг 4. Настройка приложения (application.properties)
# Quarkus Application Configuration
quarkus.application.name=user-service
quarkus.http.port=8080
# Database Schema Management (Automatically managed by Dev Services in dev mode)
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.sql=true
# SmallRye OpenAPI & Swagger UI Configuration
quarkus.smallrye-openapi.path=/swagger-ui
quarkus.swagger-ui.always-include=true
Обратите внимание, что мы не настраивали URL-адреса базы данных, имена пользователей и пароли! Когда вы запускаете ./mvnw quarkus:dev, Quarkus автоматически обнаруживает PostgreSQL, запускает контейнер Docker, настраивает таблицы схемы базы данных и открывает пользовательский интерфейс Swagger по адресу http://localhost:8080/swagger-ui.
Шаг 5. Создание и выполнение собственных исполняемых файлов
Чтобы запустить мгновенный режим разработки Live-Coding:
./mvnw quarkus:dev
Чтобы создать собственный двоичный файл Linux с использованием GraalVM внутри контейнера Docker (локальная установка GraalVM не требуется):
./mvnw package -Dnative -Dquarkus.native.container-build=true
Чтобы запустить полученный бинарный файл непосредственно в вашей ОС:
./target/user-service-1.0.0-SNAPSHOT-runner
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2026-08-26 01:37:15,102 INFO [io.quarkus] (main) user-service 1.0.0-SNAPSHOT native (powered by Quarkus 3.15.1) started in 0.016s. Listening on: http://0.0.0.0:8080
2026-08-26 01:37:15,103 INFO [io.quarkus] (main) Profile prod activated.
2026-08-26 01:37:15,103 INFO [io.quarkus] (main) Installed features: [cdi, hibernate-reactive, panache, reactive-pg-client, resteasy-reactive, resteasy-reactive-jackson, smallrye-openapi]
Запуск 0,016 секунды!
8. Сравнение стратегических функций: Quarkus и Spring Boot
| Возможность/Функция | Традиционная весенняя загрузка 3.x | Кваркус 3.x |
|---|---|---|
| Основная архитектура | Отражение во время выполнения и динамическое сканирование | Обработка во время сборки и оптимизация AOT |
| Встроенная компиляция | Spring Native (требуются сложные подсказки) | Первоклассная встроенная интеграция GraalVM |
| Скорость запуска (исходная) | ~0,1–0,5 с | ~0,01–0,04 с |
| Память RSS-следа | 140 МБ - 300 МБ | 13–40 МБ |
| Среда разработки | Горячая замена через DevTools (ограничено) | Живое кодирование без перезапуска (quarkus:dev) |
| Сторонние услуги | Ручная настройка Docker/Testcontainers | Услуги автоматической разработки (контейнеры с нулевой конфигурацией) |
| Поддержка стандартов | Весенняя экосистема | Стандартные характеристики Jakarta EE и MicroProfile |
| Реактивная парадигма | Spring WebFlux (отдельный стек) | Унифицированный движок (ядро Vert.x/реактивный + императивный) |
9. Заключение: пора ли переходить на Quarkus?
Quarkus — это не просто еще один веб-фреймворк; он представляет собой эволюцию Java в эпоху облачных технологий. Сочетая оптимизацию времени сборки со встроенной компиляцией GraalVM, Quarkus опровергает устаревший стереотип о том, что Java слишком медленная или слишком перегруженная памятью для современных микросервисов и бессерверных функций.
Когда следует выбирать Quarkus?
- Бессерверные и управляемые событиями приложения. Если вы развертываете микросервисы в AWS Lambda, GCP Cloud Run или Knative, встроенные двоичные файлы Quarkus полностью устраняют проблемы с холодным запуском.
- Кластеры Kubernetes с высокой плотностью. Если в расходах на инфраструктуру преобладает потребление оперативной памяти кластера, миграция сервисов на Quarkus может сократить затраты на память до 75 %.
- Реактивные микросервисы. Если вы создаете системы с высокой пропускной способностью, требующие неблокируемой потоковой передачи (Kafka, gRPC, WebSockets), Quarkus сразу же обеспечивает высочайший уровень производительности.
Java больше не привязана к медленным запускам или раздутой среде выполнения. С помощью Quarkus разработчики Java могут создавать облачные приложения с сверхзвуковой скоростью и субатомным объемом.
Tags
Empower Your Digital Presence & Workflows
Explore top-tier tools built by Ghaznix to streamline your links, surveys, and brand growth.