Kora облачно ориентированный серверный фреймворк написанный на Java для написания Java / Kotlin приложений с упором на производительность, эффективность, прозрачность сделанный выходцами из Т-Банк / Тинькофф

Kora is a cloud-oriented server-side Java framework for writing Java / Kotlin applications with a focus on performance, efficiency and transparency

Перейти к содержанию

Пробы с Kora

Это руководство фокусируется только на пробах. Вы добавите пути жизнеспособности и готовности на приватный порт, создадите собственную пробу готовности с периодом прогрева и проверите, как приложение сообщает платформе свое эксплуатационное состояние.

Если в процессе захочется сверить результат, используйте готовое рабочее приложение: Kora Java Observability App.

Если в процессе захочется сверить результат, используйте готовое рабочее приложение: Kora Kotlin Observability App.

Что вы создадите

Вы добавите:

  • приватный путь /system/liveness
  • приватный путь /system/readiness
  • простую пробу жизнеспособности процесса
  • пробу готовности, которая возвращает ошибку во время прогрева
  • проверку поведения проб через curl

Что понадобится

  • JDK 17 или новее
  • Gradle 7+
  • Docker, если хотите локально запустить проверку по принципу черного ящика
  • текстовый редактор или среда разработки
  • пройденное руководство по HTTP-серверу

Требования

Обязательная основа

Это руководство предполагает, что вы уже прошли руководство по HTTP-серверу и у вас уже есть HTTP-контроллеры, DTO, репозиторий, служба и конфигурация из того руководства.

Если вы еще не прошли руководство по HTTP-серверу, сначала сделайте это, потому что это руководство по наблюдаемости сохраняет тот же HTTP-интерфейс и накладывает поверх него телеметрию.

Обзор

Пробы - это маленькие проверки здоровья приложения. Они не нужны обычному пользователю и не описывают бизнес-API. Их читает платформа: Kubernetes, балансировщик, система развертывания или локальная дымовая проверка. Платформа задает приложению простой вопрос и принимает решение: отправлять трафик, подождать или перезапустить процесс.

Самое важное - различать два вопроса. Проба жизнеспособности спрашивает: "процесс жив или его нужно перезапустить?". Проба готовности спрашивает: "этот экземпляр прямо сейчас готов принимать трафик?". Ответы могут быть разными. Приложение может быть живым, но еще не готовым: например, оно прогревает кеш, ждет обязательный внешний сервис или заканчивает стартовую инициализацию.

В этом руководстве используются такие сущности:

  • LivenessProbe описывает проверку жизнеспособности процесса
  • ReadinessProbe описывает проверку готовности к трафику
  • LivenessProbeFailure и ReadinessProbeFailure сообщают причину нездорового состояния
  • приватные HTTP-пути отдают результат платформе

Kora находит компоненты проб в графе. Вы создаете обычный @Component, реализуете нужный интерфейс, а HTTP-сервер использует эти компоненты для ответа на приватных путях проверки здоровья.

Модель пробы

Проба возвращает либо null, либо объект ошибки. null означает: "все хорошо". Ошибка означает: "проверка не прошла, вот короткая причина". Это очень простая модель, и в этом ее сила.

Проба жизнеспособности должна быть осторожной. Если она возвращает ошибку, платформа может решить, что процесс завис или сломан, и перезапустить его. Поэтому проба жизнеспособности обычно не проверяет все внешние системы подряд. Краткий сбой сети не должен автоматически превращаться в перезапуск приложения.

Проба готовности может быть строже. Если она возвращает ошибку, платформа обычно просто перестает слать трафик на этот экземпляр. Процесс продолжает жить, и через некоторое время проба готовности может снова стать успешной. Это хорошо подходит для прогрева, временной недоступности зависимости или обслуживания.

В этом руководстве проба готовности моделирует прогрев через WARMUP_PERIOD. Первые миллисекунды приложение говорит: "я живое, но еще греюсь". Потом возвращает null, и экземпляр становится готовым.

Инструменты

LivenessProbe и ReadinessProbe - это Kora-интерфейсы. Они маленькие специально: у каждого есть метод probe(), а результат легко понять без дополнительной инфраструктуры.

ApplicationHealthProbe в примере является простой пробой жизнеспособности. Она возвращает null, потому что минимальное приложение считается живым, если процесс запущен и граф работает. Это не бесполезная проверка: сама доступность пути уже показывает, что приватный HTTP-сервер отвечает.

CustomReadinessProbe показывает, как добавить предметную логику готовности. В примере это прогрев по времени, но в настоящем сервисе здесь может быть проверка кеша, миграций, подключения к обязательной зависимости или внутреннего состояния фонового обработчика.

Приватный порт 8085 отделяет эксплуатационные проверки от публичного API. Клиентам не нужно знать, как приложение сообщает свое здоровье платформе. Платформе, наоборот, нужен стабильный закрытый путь, который можно часто и автоматически вызывать.

Эксплуатационная семантика

Думайте о пробах как о договоре между приложением и платформой. Приложение не говорит длинную историю. Оно коротко сообщает состояние, а платформа действует по правилам.

Типичная логика такая:

  • проба жизнеспособности успешна: процесс не нужно перезапускать
  • проба жизнеспособности неуспешна: процесс можно перезапустить
  • проба готовности успешна: экземпляр можно включить в балансировку
  • проба готовности неуспешна: экземпляр временно не должен получать трафик

Поэтому не стоит складывать все проверки в одну кучу. Если база данных недоступна на секунду, проба готовности может стать неуспешной, чтобы трафик подождал. Но проба жизнеспособности не обязана падать, если процесс сам не сломан.

Практический результат этой главы: приложение получает два понятных приватных пути. Один защищает от зависших процессов, другой помогает платформе аккуратно управлять трафиком во время старта и временных проблем.

Зависимости

Для базовых проб достаточно модулей HTTP-сервера и общих компонентов Kora из приложения HTTP-сервера. Если вы уже прошли руководство по HTTP-серверу, отдельная зависимость обычно не нужна.

dependencies {
    // ... существующие зависимости из руководства по HTTP-серверу ...
}
dependencies {
    // ... существующие зависимости из руководства по HTTP-серверу ...
}

Модули

Оставьте основной граф HTTP-приложения. Пробы будут найдены как обычные компоненты графа.

@KoraApp
public interface Application extends
        HoconConfigModule,
        JsonModule,
        LogbackModule,
        UndertowHttpServerModule {  // <----- Подключили модуль

    static void main(String[] args) {
        KoraApplication.run(ApplicationGraph::graph);
    }
}
@KoraApp
interface Application :
    HoconConfigModule,
    JsonModule,
    LogbackModule,
    UndertowHttpServerModule  // <----- Подключили модуль

Конфигурация

Разместите пути проб на приватном порту:

src/main/resources/application.conf
httpServer {
  publicApiHttpPort = 8080
  privateApiHttpPort = 8085
  privateApiHttpLivenessPath = "/system/liveness"
  privateApiHttpReadinessPath = "/system/readiness"
}

Оставляйте пути явными. Это упрощает настройку Docker Compose, проб Kubernetes и локальных дымовых проверок.

Проба жизнеспособности

Проба жизнеспособности должна быть консервативной. Если она возвращает ошибку, платформа может перезапустить процесс. В минимальном приложении можно вернуть null, то есть процесс жив.

@Component
public final class ApplicationHealthProbe implements LivenessProbe {

    @Override
    public LivenessProbeFailure probe() {
        return null;
    }
}
@Component
class ApplicationHealthProbe : LivenessProbe {
    override fun probe(): LivenessProbeFailure? = null
}

Не проверяйте в пробе жизнеспособности все внешние зависимости подряд. Иначе краткая проблема с сетью может превратиться в лишние перезапуски.

Проба готовности

Проба готовности может быть строже. Она сообщает, готов ли экземпляр принимать трафик прямо сейчас. Для демонстрации сделаем период прогрева.

@Component
public final class CustomReadinessProbe implements ReadinessProbe {

    private static final Duration WARMUP_PERIOD = Duration.ofMillis(500);

    private final Instant startedAt = Instant.now();

    @Override
    public ReadinessProbeFailure probe() {
        var readyAt = startedAt.plus(WARMUP_PERIOD);
        if (Instant.now().isBefore(readyAt)) {
            return new ReadinessProbeFailure("Service is warming up");
        }
        return null;
    }
}
@Component
class CustomReadinessProbe : ReadinessProbe {
    private val startedAt = Instant.now()

    override fun probe(): ReadinessProbeFailure? {
        val readyAt = startedAt.plus(Duration.ofMillis(500))
        return if (Instant.now().isBefore(readyAt)) {
            ReadinessProbeFailure("Service is warming up")
        } else {
            null
        }
    }
}

В реальном приложении проба готовности часто проверяет прогрев кешей, доступность обязательных нижестоящих сервисов или готовность миграций. Но смысл остается тот же: если ошибка временная, трафик должен подождать, а процесс не обязательно нужно перезапускать.

Проверка приложения

Запустите приложение и проверьте приватные пути:

curl -i http://localhost:8085/system/liveness
curl -i http://localhost:8085/system/readiness

Сразу после старта проба готовности может вернуть ошибку прогрева. Через короткое время повторный запрос должен стать успешным:

sleep 1
curl -i http://localhost:8085/system/readiness

Публичный порт 8080 при этом остается для бизнес-API, а не для путей проверки здоровья.

Лучшие практики

  • Делайте пробу жизнеспособности простой и устойчивой к кратким внешним сбоям.
  • Используйте пробу готовности для прогрева и временной недоступности зависимостей.
  • Держите пути проб на приватном порту.
  • Возвращайте понятное сообщение об ошибке.
  • Проверяйте пробы локально до настройки манифестов Kubernetes.

Итоги

Вы добавили пути жизнеспособности и готовности, реализовали собственные компоненты проб и проверили поведение прогрева.

Ключевые понятия

Liveness: : сигнал, что процесс жив и не требует рестарта.

Readiness: : сигнал, что экземпляр готов принимать трафик.

Закрытый управляющий порт: : закрытый порт для эксплуатационных путей.

Ошибка пробы: : короткое сообщение о причине нездорового состояния.

Устранение неполадок

Путь не открывается: : Проверьте privateApiHttpPort, privateApiHttpLivenessPath и privateApiHttpReadinessPath.

Проба готовности всегда неуспешна: : Проверьте условие внутри CustomReadinessProbe.

Приложение часто рестартует: : Убедитесь, что внешние зависимости проверяются пробой готовности, а не пробой жизнеспособности.

Что дальше?

Помощь

  • сравните код проб с готовыми Java и Kotlin приложениями наблюдаемости
  • проверьте семантику жизнеспособности и готовности в документации по пробам