Hello world¶
Данный пример разбирает как создать простой сервис на kora, с настроенными метриками, логированием и пробами, который умеет отвечать на запрос GET /hello/world
.
Подготовка¶
Создаем новый Gradle-проект (через IDEA или gradle init
).
Для работы нам потребуется gradlew с настроенной версией Gradle выше 7.*
.
Проверим конфигурацию в gradle/wrapper/gradle-wrapper.properties
:
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
Настройка Gradle¶
Kotlin¶
Начиная с 9-й версии можно использовать ksp для проектов на Kotlin.
build.gradle.kts
:
plugins {
kotlin("jvm") version "1.7.10"
id("com.google.devtools.ksp") version "1.7.10-1.0.6"
}
group = "ru.tinkoff.kora.hello.world"
repositories {
mavenCentral()
}
kotlin {
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of("17"))
}
// объяснем идее где лежат сгенеренные классы
sourceSets.main {
kotlin.srcDir("build/generated/ksp/main/kotlin")
}
sourceSets.test {
kotlin.srcDir("build/generated/ksp/test/kotlin")
}
}
val koraVersion = "0.10.1"
dependencies {
val kora = platform("ru.tinkoff.kora:kora-parent:$koraVersion")
implementation(kora)
ksp(kora)
ksp("ru.tinkoff.kora:symbol-processors")
implementation("ru.tinkoff.kora:http-server-undertow")
implementation("ru.tinkoff.kora:micrometer-module")
implementation("ru.tinkoff.kora:json-module")
}
Groovy¶
build.gradle
:
plugins {
id 'java'
}
group 'ru.tinkoff.kora.hello.world'
repositories {
mavenCentral()
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
var koraVersion = '0.10.1'
dependencies {
implementation platform("ru.tinkoff.kora:kora-parent:$koraVersion")
implementation "ru.tinkoff.kora:http-server-undertow"
implementation "ru.tinkoff.kora:json-module"
implementation "ru.tinkoff.kora:micrometer-module"
annotationProcessor "ru.tinkoff.kora:annotation-processors:$koraVersion"
}
Минимальная конфигурация приложения¶
Для запуска приложения нам нужно сформировать контейнер. Для этого создадим интерфейс Application.java с таким кодом:
package ru.tinkoff.kora.hello.world;
import ru.tinkoff.kora.common.KoraApp;
import ru.tinkoff.kora.config.common.ConfigModule;
import ru.tinkoff.kora.http.server.undertow.UndertowHttpServerModule;
import ru.tinkoff.kora.micrometer.module.MetricsModule;
@KoraApp
public interface Application extends
ConfigModule,
MetricsModule,
UndertowHttpServerModule {
}
Если мы запустим компиляцию, то будет сгенерирован класс ApplicationGraph.java
, в котором описано как собирать все компоненты нашего будущего контейнера.
Что нам предоставляет UndertowHttpServerModule
:
- Сервер для публичного апи на порту 8080
- Сервер для системного апи на порту 8085
- liveness и readiness пробы на системном порту: /system/liveness и /system/readiness
- route отдающий метрики: /metrics
Далее нам нужно создать точку входа, создадим класс AppRunner.java
с методом main
:
package ru.tinkoff.kora.hello.world;
import ru.tinkoff.kora.application.graph.KoraApplication;
public class AppRunner {
public static void main(String[] args) {
KoraApplication.run(ApplicationGraph::graph);
}
}
KoraApplication.run
запускает параллельную инициализацию всех компонентов в контейнере и блокирует основной поток до получения сигнала SIGTERM, после этого приложение начинает graceful shutdown.
Теперь, если мы запустим это приложение, то нам будут доступны роуты по ссылкам выше.
При этом в логах видно, что поднялся только порт 8085. Это происходит из-за того, что у нас ещё нет ни одного обработчика запросов.
Hello world контроллер¶
Теперь давайте напишем контроллер, который будет обрабатывать запрос GET /hello/world
на публичном порту.
package ru.tinkoff.kora.hello.world;
import ru.tinkoff.kora.common.Component;
import ru.tinkoff.kora.http.common.HttpHeaders;
import ru.tinkoff.kora.http.common.HttpMethod;
import ru.tinkoff.kora.http.common.annotation.HttpRoute;
import ru.tinkoff.kora.http.server.common.HttpServerResponse;
import ru.tinkoff.kora.http.server.common.SimpleHttpServerResponse;
import ru.tinkoff.kora.http.server.common.annotation.HttpController;
import java.nio.charset.StandardCharsets;
@Component
@HttpController
public final class HelloWorldController {
@HttpRoute(method = HttpMethod.GET, path = "/hello/world")
public HttpServerResponse helloWorld() {
return new SimpleHttpServerResponse(
200,
"text/plain",
HttpHeaders.of(),
StandardCharsets.UTF_8.encode("Hello world")
);
}
}
Давайте разберёмся детально:
- HttpController - говорит, что класс - это контроллер
- Component - говорит, что мы хотим добавить этот класс в наш контейнер
- HttpRoute - описывает какой роут хотим обрабатывать
- HttpServerResponse - это сырой вариант ответа, в котором можно выставить что угодно и отдать любые байты
Ответ в формате json¶
В обычной жизни мы всё-таки чаще отдаём json, для этого добавим модуль JsonModule
:
package ru.tinkoff.kora.hello.world;
import ru.tinkoff.kora.common.KoraApp;
import ru.tinkoff.kora.config.common.ConfigModule;
import ru.tinkoff.kora.http.server.undertow.UndertowHttpServerModule;
import ru.tinkoff.kora.json.module.JsonModule;
import ru.tinkoff.kora.micrometer.module.MetricsModule;
@KoraApp
public interface Application extends
ConfigModule,
MetricsModule,
JsonModule,
UndertowHttpServerModule {
}
И изменим контроллер, чтобы он возвращал DTO, который мы хотим сериализовать:
package ru.tinkoff.kora.hello.world;
import ru.tinkoff.kora.common.Component;
import ru.tinkoff.kora.http.common.HttpMethod;
import ru.tinkoff.kora.http.common.annotation.HttpRoute;
import ru.tinkoff.kora.http.server.common.annotation.HttpController;
import ru.tinkoff.kora.json.common.annotation.Json;
@Component
@HttpController
public final class HelloWorldController {
record HelloWorldResponse(String greeting) {}
@HttpRoute(method = HttpMethod.GET, path = "/hello/world")
@Json
public HelloWorldResponse helloWorld() {
return new HelloWorldResponse("hello world");
}
}
Теперь для нашего DTO будет сформирован оптимальный JsonWriter и в ответе мы увидим json.
{"greeting":"Hello world"}