Что такое 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 и т.д.)
- Второй параметр — лямбда-функция.
Пример:
Также в библиотеке
Машинное обучение на Go: обзор библиотек и фреймворков (Golang)
Машинное обучение на Kotlin: обзор библиотек KMath, Smile, KotlinDL и DeepLearning4J
Stripe для JavaScript (Node.js): полное руководство с примерами
Работа с датами в Kotlin: библиотеки и инструменты для разработки
Машинное обучение на C#: библиотеки ML.NET и Accord.NET
Рекомендаций