Советы по работе с Quarkus
В этой статье вы узнаете несколько полезных советов и приемов, связанных с фреймворком Quarkus. Мы сосредоточимся на функциях, которые выделяют Quarkus среди других Java фреймворков.
Для тех, кто использует Spring Boot, есть аналогичная статья - Spring Boot Tips, Tricks and Techniques.
Это перевод статьи Quarkus Tips, Tricks and Techniques, автор Piotr Minkowski.
Если вы запускаете свои приложения в Kubernetes, то Quarkus, безусловно, будет хорошим выбором в качестве Java фреймворка. У приложений на Quarkus довольно высокая скорость запуска, а также низкое потребление памяти. Вы можете легко скомпилировать своё приложение с помощью GraalVM в нативный бинарный образ. Фреймворк предоставляет множество полезных функций для разработчиков, например, горячая перезагрузка. Я надеюсь, что вы найдете в этой статье советы и приёмы, которые помогут повысить вашу продуктивность при разработке на Quarkus. Или, может быть, это хотя бы убедит вас взглянуть на этот фреймворк, если у вас еще нет опыта работы с ним.
Содержание
- Совет 1. Используйте командную строку Quarkus
- Совет 2. Используйте Dev Services при работе с базами данных
- Совет 3. Используйте упрощенный ORM - Panache
- Совет 4. Единая конфигурация
- Совет 5. Развертывание в Kubernetes с Maven
- Совет 6. Доступ к консоли Dev UI
- Совет 7. Непрерывное тестирование
- Совет 8. Собирайте нативные образы с GraalVM в OpenShift
- Совет 9. Откат транзакции после каждого теста
- Совет 10. Воспользуйтесь преимуществами поддержки GraphQL
- Заключение
Я уже публиковал все эти советы в Твиттере в графической форме, показанной ниже. Вы можете получить к ним доступ, используя поиск по хэштегу #QuarkusTips. Я большой поклонник Quarkus (и, честно говоря, Spring Boot тоже :)). Итак, если у вас есть предложения или ваши собственные любимые функции, просто напишите мне в Twitter (@piotr_minkowski). Я обязательно сделаю ретвит вашего твита.
Совет 1. Используйте командную строку Quarkus
Как сконфигурировать новое приложение при использовании какого-нибудь популярного Java фреймворка? Вы можете перейти на сайт онлайн-генератора, который обычно предоставляется этими фреймворками. Вы слышали о Spring Initializr? Quarkus предлагает аналогичный сайт, доступный по адресу https://code.quarkus.io/. Но, возможно, вы не знаете, что существует также и инструмент Quarkus CLI. Он позволяет создавать проекты, управлять расширениями и выполнять различные команды, необходимые для сборки, тестирования и запуска приложения. Например, вы можете создать исходный шаблон для нового приложения с помощью всего одной команды, как показано ниже.
$ quarkus create app --package-name=pl.piomin.samples.quarkus \
-x resteasy-jackson,hibernate-orm-panache,jdbc-postgresql \
-o person-service \
pl.piomin.samples:person-service
После выполнения этой команды вы увидите такой результат:
Эта команда создает простое REST приложение, которое использует базу данных PostgreSQL при помощи ORM Quarkus. Кроме того, она задаёт имя приложения, Maven groupId и artifactId. После этого вы можете просто запустить приложение. Для этого перейдите в созданный каталог и выполните следующую команду. В качестве альтернативы вы можете выполнить команду mvn quarkus: dev
.
$ quarkus dev
Приложение не запускается, так как не настроено соединение с базой данных. Мы должны это делать? Нет! Давайте перейдем к следующему разделу, чтобы понять, почему.
Совет 2. Используйте Dev Services при работе с базами данных
Вы слышали о Testcontainers? Это Java-библиотека, которая позволяет автоматически запускать контейнеры во время тестов. Вы можете запускать различные базы данных, Selenium или что угодно еще, что может работать в контейнере Docker. Quarkus обеспечивает встроенную интеграцию с Testcontainers при запуске приложений в режиме разработки или тестирования. Эта функция называется Dev Services. Более того, вам не нужно ничего делать, чтобы её включить. Просто НЕ ПРЕДОСТАВЛЯЙТЕ URL-адрес для подключения к базе данных и учетные данные!
Вернемся к нашему сценарию. Мы уже создали приложение с использованием Quarkus CLI. Оно содержит все необходимые библиотеки. Итак, единственное, что нам теперь нужно сделать, это запустить демон Docker. Благодаря этому Quarkus попытается запустить PostgreSQL с Testcontainers в режиме разработки. Что в итоге? Наше приложение работает и связано с PostgreSQL, запущенным в Docker, как показано ниже.
Итак, мы можем перейти непосредственно к разработке. С помощью команды quarkus dev
мы уже включили режим разработки. Благодаря этому мы можем воспользоваться функцией перезагрузки в реальном времени.
Совет 3. Используйте упрощенный ORM - Panache
Давайте добавим немного больше кода в наше приложение. Мы реализуем уровень доступа к данным с помощью Quarkus Panache ORM. Это очень интересный модуль, который фокусируется на том, чтобы сделать создание сущностей модеди данных тривиальным и удобным. Вот наш класс сущности.
@Entity
public class Person extends PanacheEntity {
public String name;
public int age;
@Enumerated(EnumType.STRING)
public Gender gender;
}
Благодаря тому, что обработкой доступа к полям управляет Quarkus, когда вы обращаетесь к person.name
, вы фактически вызываете геттер getName()
, и аналогично для записи полей и сеттеров. Это обеспечивает правильную инкапсуляцию во время выполнения, поскольку все обращения к полям будут заменены соответствующими вызовами геттеров или сеттеров. PanacheEntity также заботится о реализации первичного ключа.
На следующем шаге мы собираемся определить класс репозитория. Поскольку он реализует интерфейс PanacheRepository, нам нужно только добавить наши собственные методы поиска.
@ApplicationScoped
public class PersonRepository implements PanacheRepository<Person> {
public List<Person> findByName(String name) {
return find("name", name).list();
}
public List<Person> findByAgeGreaterThan(int age) {
return find("age > ?1", age).list();
}
}
Наконец, давайте добавим класс REST ресурса.
@Path("/persons")
public class PersonResource {
@Inject
PersonRepository personRepository;
@POST
@Transactional
public Person addPerson(Person person) {
personRepository.persist(person);
return person;
}
@GET
public List<Person> getPersons() {
return personRepository.listAll();
}
@GET
@Path("/name/{name}")
public List<Person> getPersonsByName(@PathParam("name") String name) {
return personRepository.findByName(name);
}
@GET
@Path("/age-greater-than/{age}")
public List<Person> getPersonsByName(@PathParam("age") int age) {
return personRepository.findByAgeGreaterThan(age);
}
@GET
@Path("/{id}")
public Person getPersonById(@PathParam("id") Long id) {
return personRepository.findById(id);
}
}
Кроме того, давайте создадим файл import.sql
в каталоге src/main/resources
. Он содержит SQL команды для создания тестовых записей в базе данных и будет выполнен при запуске Hibernate ORM.
insert into person(id, name, age, gender) values(1, 'John Smith', 25, 'MALE');
insert into person(id, name, age, gender) values(2, 'Paul Walker', 65, 'MALE');
insert into person(id, name, age, gender) values(3, 'Lewis Hamilton', 35, 'MALE');
insert into person(id, name, age, gender) values(4, 'Veronica Jones', 20, 'FEMALE');
insert into person(id, name, age, gender) values(5, 'Anne Brown', 60, 'FEMALE');
insert into person(id, name, age, gender) values(6, 'Felicia Scott', 45, 'FEMALE');
Наконец, мы можем вызвать наш REST сервис.
$ curl http://localhost:8080/persons
Совет 4. Единая конфигурация
Предположим, что мы не хотим запускать базу данных в Docker, тогда мы должны сконфигурировать соединение в application.properties
файле. По умолчанию Quarkus предоставляет 3 профиля: prod, test, dev. Мы можем определить свойства для нескольких профилей внутри одного application.properties
, используя синтаксис %{profile-name}.config.name
. В нашем случае пусть у нас будет экземпляр базы данных H2, который будет использоваться в 2 профилях - dev и test, и внешний экземпляр PostgreSQL, используемый в профиле prod.
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=${POSTGRES_USER}
quarkus.datasource.password=${POSTGRES_PASSWORD}
quarkus.datasource.jdbc.url=jdbc:postgresql://person-db:5432/${POSTGRES_DB}
%test.quarkus.datasource.db-kind=h2
%test.quarkus.datasource.username=sa
%test.quarkus.datasource.password=password
%test.quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb
%dev.quarkus.datasource.db-kind=h2
%dev.quarkus.datasource.username=sa
%dev.quarkus.datasource.password=password
%dev.quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb
Перед запуском новой версии приложения мы должны не забыть добавить Maven зависимость для H2 в pom.xml
.
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2</artifactId>
</dependency>
Вы также можете определить свой собственный профиль и указать свойства, используя его в качестве префикса. А также вы можете определять отдельные файлы конфигурации для конкретного профиля, такие как application-{profile}.properties
.
Совет 5. Развертывание в Kubernetes с Maven
Quarkus это фреймворк, который изначально создавался для работы в Kubernetes. Вы можете легко развернуть приложение на Quarkus в кластере Kubernetes, не создавая файлы YAML вручную. Для более сложных конфигураций, например, сопоставления секретов с переменными среды, вы можете использовать application.properties
. Для работы с Kubernetes нам нужно включить модуль quarkus-kubernetes
. Также есть отдельная реализация для OpenShift.
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-openshift</artifactId>
</dependency>
После этого Quarkus будет генерировать манифесты развертывания во время сборки при помощи Maven. Мы можем включить автоматическое развертывание в текущий кластер Kubernetes, установив для свойства quarkus.kubernetes.deploy
значение true
. Для развертывания в OpenShift мы должны изменить цель развертывания по умолчанию с kubernetes
на openshift
.
quarkus.container-image.build = true
quarkus.kubernetes.deploy = true
quarkus.kubernetes.deployment-target = openshift
Предположим, у нас есть некоторая пользовательская конфигурация, которую нужно установить в манифесте развертывания. Наше приложение будет работать в двух репликах и будет автоматически доступно за пределами кластера. Оно также будет использовать значения из Secret для подключения к базе данных PostgreSQL.
quarkus.openshift.expose = true
quarkus.openshift.replicas = 2
quarkus.openshift.labels.app = person-app
quarkus.openshift.annotations.app-type = demo
quarkus.openshift.env.mapping.postgres_user.from-secret = person-db
quarkus.openshift.env.mapping.postgres_user.with-key = database-user
quarkus.openshift.env.mapping.postgres_password.from-secret = person-db
quarkus.openshift.env.mapping.postgres_password.with-key = database-password
quarkus.openshift.env.mapping.postgres_db.from-secret = person-db
quarkus.openshift.env.mapping.postgres_db.with-key = database-name
Теперь всё что нам нужно, это просто собрать наше приложение при помощи Maven. В качестве альтернативы мы можем удалить свойство quarkus.kubernetes.deploy
из application.properties
и включить его в команде Maven.
$ maven clean package -D<meta charset="utf-8">quarkus.kubernetes.deploy=true
Совет 6. Доступ к консоли Dev UI
После запуска Quarkus-приложения в режиме разработки (mvn quarkus: dev
) вы можете получить доступ к консоли Dev UI по адресу http://localhost:8080/q/dev
. Чем больше модулей вы включите, тем больше параметров вы сможете настроить там. Одна из моих любимых функций - возможность развертывать приложения в OpenShift. Вместо того, чтобы запускать команду Maven для создания приложения, мы можем просто запустить его в режиме разработки и развернуть с помощью графического пользовательского интерфейса.
Совет 7. Непрерывное тестирование
Quarkus поддерживает непрерывное тестирование, при котором тесты запускаются сразу после изменения кода. Это позволяет вам мгновенно получать обратную связь об изменениях в вашем коде. Quarkus определяет, какие тесты покрывают какой код, и использует эту информацию для запуска только соответствующих тестов. После запуска разрабатываемого приложения вам будет предложено включить эту функцию, как показано ниже. Просто нажмите r
, чтобы включить этот режим.
Давайте добавим несколько тестов для нашего приложения. Во-первых, нам нужно добавить соответствующие зависимости.
<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>
Затем мы добавим несколько простых тестов API. Тестовый класс должен быть помечен аннотацией @QuarkusTest
. В остальном реализация типична для библиотеки REST Assured.
@QuarkusTest
public class PersonResourceTests {
@Test
void getPersons() {
List<Person> persons = given().when().get("/persons")
.then()
.statusCode(200)
.extract()
.body()
.jsonPath().getList(".", Person.class);
assertEquals(persons.size(), 6);
}
@Test
void getPersonById() {
Person person = given()
.pathParam("id", 1)
.when().get("/persons/{id}")
.then()
.statusCode(200)
.extract()
.body().as(Person.class);
assertNotNull(person);
assertEquals(1L, person.id);
}
@Test
void newPersonAdd() {
Person newPerson = new Person();
newPerson.age = 22;
newPerson.name = "TestNew";
newPerson.gender = Gender.FEMALE;
Person person = given()
.body(newPerson)
.contentType(ContentType.JSON)
.when().post("/persons")
.then()
.statusCode(200)
.extract()
.body().as(Person.class);
assertNotNull(person);
assertNotNull(person.id);
}
}
Мы также можем запускать эти тесты из консоли Dev UI. Для этого вам следует перейти в консоль Dev UI. Внизу страницы вы найдете модуль тестирования, который отвечает за соответствующую панель. Просто щелкните значок Test result
, и вы увидите экран, подобный изображенному ниже.
Совет 8. Собирайте нативные образы с GraalVM в OpenShift
Вы можете легко создать и запустить нативный бинарный образ вашего Quarkus-приложения используя возможности GraalVM в OpenShift. Для этого используйте всего одну команду и конструктор ubi-quarkus-native-s2i
. OpenShift создает приложение с использованием подхода S2I (source-2-image). Конечно, вам будет нужен работающий кластер OpenShift (например, локальный CRC или Developer Sandbox https://developers.redhat.com/products/codeready-containers/overview), а также oc
- клиент OpenShift, установленный локально.
$ oc new-app --name person-native \
--context-dir basic-with-db/person-app \
quay.io/quarkus/ubi-quarkus-native-s2i:21.2-java11~https://github.com/piomin/openshift-quickstart.git
Совет 9. Откат транзакции после каждого теста
Если вам нужно откатить изменения в данных после каждого теста, не делайте этого вручную. Вместо этого вам просто нужно пометить свой тестовый класс при помощи аннотации @TestTransaction
. Откат выполняется каждый раз после завершения метода тестирования.
@QuarkusTest
@TestTransaction
public class PersonRepositoryTests {
@Inject
PersonRepository personRepository;
@Test
void addPerson() {
Person newPerson = new Person();
newPerson.age = 22;
newPerson.name = "TestNew";
newPerson.gender = Gender.FEMALE;
personRepository.persist(newPerson);
Assertions.assertNotNull(newPerson.id);
}
}
Совет 10. Воспользуйтесь преимуществами поддержки GraphQL
Это последний из советов Quarkus в этой статье. Однако это одна из моих любимых функций Quarkus. Поддержка GraphQL не является сильной стороной Spring Boot. Зато Quarkus предоставляет очень мощные и удобные в использовании расширения для GraphQL для клиентской и серверной сторон.
Во-первых, давайте добавим зависимости Quarkus, отвечающие за поддержку GraphQL.
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-graphql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-graphql-client</artifactId>
<scope>test</scope>
</dependency>
Теперь мы можем создать код, отвечающий за предоставление GraphQL API. Класс должен быть помечен аннотацией @GraphQLAPI
. Quarkus автоматически генерирует схему GraphQL из исходного кода.
@GraphQLApi
public class EmployeeFetcher {
private EmployeeRepository repository;
public EmployeeFetcher(EmployeeRepository repository){
this.repository = repository;
}
@Query("employees")
public List<Employee> findAll() {
return repository.listAll();
}
@Query("employee")
public Employee findById(@Name("id") Long id) {
return repository.findById(id);
}
@Query("employeesWithFilter")
public List<Employee> findWithFilter(@Name("filter") EmployeeFilter filter) {
return repository.findByCriteria(filter);
}
}
Теперь давайте создадим клиентскую часть для работы с нашим сервисом. Нам нужно пометить этот интерфейс с помощью аннотации @GraphQLClientApi
.
@GraphQLClientApi(configKey = "employee-client")
public interface EmployeeClient {
List<Employee> employees();
Employee employee(Long id);
}
Наконец, мы можем добавить простой JUnit тест. Нам нужно всего лишь внедрить EmployeeClient и воcпользоваться его методами. Если вас интересует более подробная информация о поддержке GraphQL в Quarkus, то прочтите мою статью An Advanced GraphQL with Quarkus.
@QuarkusTest
public class EmployeeFetcherTests {
@Inject
EmployeeClient employeeClient;
@Test
void fetchAll() {
List<Employee> employees = employeeClient.employees();
Assertions.assertEquals(10, employees.size());
}
@Test
void fetchById() {
Employee employee = employeeClient.employee(10L);
Assertions.assertNotNull(employee);
}
}
Заключение
На мой взгляд, Quarkus - очень интересный и многообещающий фреймворк. С помощью этих советов вы легко сможете начать разработку своего первого приложения на Quarkus. В каждом новом релизе регулярно появляются новые интересные функции. Так что, возможно, мне скоро придется обновить этот список советов.