Resilience4j для Java: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что такое Resilience4j и зачем она нужна?

Resilience4j — это легковесная библиотека для Java, предназначенная для повышения отказоустойчивости (resilience) микросервисов и распределенных систем. Она предоставляет набор готовых компонентов, которые помогают защитить ваше приложение от каскадных сбоев, перегрузок и медленных ответов от внешних сервисов (баз данных, API, очередей сообщений и т.д.).

Основная задача библиотеки — реализовать паттерны отказоустойчивости, такие как Circuit Breaker (автоматическое отключение проблемного сервиса), Rate Limiter (ограничение частоты запросов), Retry (повторные попытки), Bulkhead (изоляция ресурсов), Time Limiter (ограничение времени выполнения) и Cache (кэширование результатов).

Библиотека идеально вписывается в экосистему Spring Boot, но может использоваться и в любом другом Java-приложении. Resilience4j считается современной заменой устаревшей библиотеке Hystrix от Netflix.

Установка (Maven/Gradle)

Resilience4j — это Java-библиотека, поэтому она устанавливается через системы сборки Maven или Gradle. В отличие от Python, команда pip install здесь не применяется.

Maven (pom.xml)

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-all</artifactId>
    <version>2.2.0</version>
</dependency>

<!-- Для Spring Boot дополнительно -->
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot3</artifactId>
    <version>2.2.0</version>
</dependency>

Gradle (build.gradle)

implementation 'io.github.resilience4j:resilience4j-all:2.2.0'
implementation 'io.github.resilience4j:resilience4j-spring-boot3:2.2.0'

Быстрый старт с примером кода

Создадим простой пример использования Circuit Breaker. Допустим, у нас есть метод, который вызывает внешний API. Мы хотим, чтобы если API отвечает с ошибкой 5 раз подряд, то следующие вызовы мгновенно возвращали fallback-значение, не нагружая проблемный сервис.

import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import java.time.Duration;
import java.util.function.Supplier;

public class QuickStartExample {

    public static void main(String[] args) {
        // 1. Создаем конфигурацию Circuit Breaker
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
                .failureRateThreshold(50) // Порог ошибок в процентах
                .waitDurationInOpenState(Duration.ofMillis(1000)) // Время в открытом состоянии
                .slidingWindowSize(5) // Размер скользящего окна для подсчета ошибок
                .build();

        // 2. Создаем реестр и Circuit Breaker
        CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(config);
        CircuitBreaker circuitBreaker = registry.circuitBreaker("myService");

        // 3. Оборачиваем опасный вызов
        Supplier<String> decoratedSupplier = CircuitBreaker
                .decorateSupplier(circuitBreaker, QuickStartExample::riskyOperation);

        // 4. Выполняем несколько раз
        for (int i = 0; i < 10; i++) {
            try {
                String result = decoratedSupplier.get();
                System.out.println("Успех: " + result);
            } catch (Exception e) {
                System.out.println("Ошибка: " + e.getMessage());
            }
        }
    }

    // Имитация ненадежного внешнего сервиса
    public static String riskyOperation() {
        if (Math.random() > 0.3) {
            throw new RuntimeException("Сервис временно недоступен!");
        }
        return "Данные получены";
    }
}

Основные методы и функции

1. CircuitBreaker (Автомат прерывания)

Что делает: Отслеживает количество ошибок. Если порог превышен, переводит вызовы в состояние OPEN (открыто), и все последующие запросы мгновенно завершаются с исключением или fallback-значением. Через заданное время переходит в HALF_OPEN для проверки восстановления сервиса.

Параметры:

  • failureRateThreshold — порог ошибок в процентах (например, 50).
  • waitDurationInOpenState — время ожидания перед переходом в HALF_OPEN.
  • slidingWindowSize — размер окна для подсчета статистики.
  • minimumNumberOfCalls — минимальное количество вызовов перед расчетом.

Пример:

CircuitBreakerConfig config = CircuitBreakerConfig.custom()
    .failureRateThreshold(40)
    .waitDurationInOpenState(Duration.ofSeconds(5))
    .slidingWindowSize(10)
    .build();

CircuitBreaker circuitBreaker = CircuitBreakerRegistry.of(config)
    .circuitBreaker("database");

// Оборачиваем вызов
Supplier<String> supplier = CircuitBreaker.decorateSupplier(circuitBreaker, () -> {
    // вызов к базе данных
    return "OK";
});

// Получаем результат
Try<String> result = Try.ofSupplier(supplier);
System.out.println(result.isSuccess());

2. Retry (Повторные попытки)

Что делает: Автоматически повторяет выполнение операции при возникновении исключения. Позволяет настроить количество попыток, интервал между ними и политику увеличения задержки (backoff).

Параметры:

  • maxAttempts — максимальное количество попыток (включая первую).
  • waitDuration — время ожидания между попытками.
  • intervalFunction — функция для расчета интервала (например, exponential backoff).
  • retryExceptions — список исключений, при которых нужно повторять.

Пример:

RetryConfig config = RetryConfig.custom()
    .maxAttempts(3)
    .waitDuration(Duration.ofMillis(500))
    .retryExceptions(RuntimeException.class)
    .build();

Retry retry = Retry.of("apiCall", config);

Supplier<String> retryableSupplier = Retry.decorateSupplier(retry, () -> {
    if (Math.random() > 0.5) throw new RuntimeException("Ошибка");
    return "Успех";
});

String result = retryableSupplier.get(); // будет повторено до 3 раз

3. RateLimiter (Ограничитель скорости)

Что делает: Ограничивает количество запросов за определенный промежуток времени. Защищает сервис от перегрузки (throttling).

Параметры:

  • limitForPeriod — количество разрешенных запросов за период.
  • limitRefreshPeriod — продолжительность периода (например, 1 секунда).
  • timeoutDuration — время ожидания, если лимит исчерпан.

Пример:

RateLimiterConfig config = RateLimiterConfig.custom()
    .limitForPeriod(10) // 10 запросов
    .limitRefreshPeriod(Duration.ofSeconds(1)) // за 1 секунду
    .timeoutDuration(Duration.ofMillis(100)) // ждать не более 100мс
    .build();

RateLimiter rateLimiter = RateLimiter.of("myLimiter", config);

Supplier<String> limitedSupplier = RateLimiter.decorateSupplier(rateLimiter, () -> "OK");

for (int i = 0; i < 15; i++) {
    try {
        System.out.println(limitedSupplier.get());
    } catch (RequestNotPermitted e) {
        System.out.println("Превышен лимит: " + e.getMessage());
    }
}

4. TimeLimiter (Ограничитель времени)

Что делает: Устанавливает максимальное время выполнения операции. Если операция не завершилась за указанное время, выбрасывается исключение TimeoutException.

Параметры:

  • timeoutDuration — максимальное время выполнения.
  • cancelRunningFuture — отменять ли выполняющуюся задачу при таймауте.

Пример:

TimeLimiterConfig config = TimeLimiterConfig.custom()
    .timeoutDuration(Duration.ofSeconds(2))
    .cancelRunningFuture(true)
    .build();

TimeLimiter timeLimiter = TimeLimiter.of(config);

// Оборачиваем CompletableFuture
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try { Thread.sleep(3000); } catch (InterruptedException e) { }
    return "Готово";
});

Supplier<CompletableFuture<String>> decoratedFuture = TimeLimiter
    .decorateCompletionStage(timeLimiter, () -> future);

// Вызов с таймаутом
CompletableFuture<String> result = decoratedFuture.get();
result.whenComplete((res, ex) -> {
    if (ex != null) System.out.println("Таймаут: " + ex.getMessage());
    else System.out.println("Результат: " + res);
});

5. Bulkhead (Перегородка)

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

Параметры:

  • maxConcurrentCalls — максимальное количество одновременных вызовов.
  • maxWaitDuration — максимальное время ожидания в очереди.

Пример:

BulkheadConfig config = BulkheadConfig.custom()
    .maxConcurrentCalls(3)
    .maxWaitDuration(Duration.ofMillis(500))
    .build();

Bulkhead bulkhead = Bulkhead.of("myBulkhead", config);

Supplier<String> decoratedSupplier = Bulkhead.decorateSupplier(bulkhead, () -> {
    Thread.sleep(1000);
    return "OK";
});

// Запускаем 10 потоков
for (int i = 0; i < 10; i++) {
    new Thread(() -> {
        try {
            System.out.println(decoratedSupplier.get());
        } catch (BulkheadFullException e) {
            System.out.println("Bulkhead переполнен: " + e.getMessage());
        }
    }).start();
}

6. Cache (Кэш)

Что делает: Кэширует результаты вызовов на основе ключа. Если результат уже есть в кэше, операция не выполняется повторно.

Параметры:

  • cache — реализация интерфейса javax.cache.Cache.

Пример:

// Создаем кэш (например, Caffeine)
Cache<String, String> cache = Caffeine.newBuilder()
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .maximumSize(100)
    .build();

// Оборачиваем вызов
Supplier<String> cachedSupplier = Cache.decorateSupplier(cache, "myKey", () -> {
    System.out.println("Выполняем дорогую операцию...");
    return "Результат";
});

System.out.println(cachedSupplier.get()); // Выполнит операцию
System.out.println(cachedSupplier.get()); // Возьмет из кэша

7. decorateSupplier / decorateConsumer / decorateRunnable

Что делает: Базовые методы для оборачивания различных типов функций (Supplier, Consumer, Runnable, Function, etc.) в декораторы Resilience4j.

Параметры:

  • Первый параметр — компонент (CircuitBreaker, Retry и т.д.)
  • Второй параметр — лямбда-функция.

Пример:


                
    
    

Рекомендаций