Библиотека отказоустойчивости на чистой Java с использованием Reflection API.
@Retry(
maxAttempts = 4, // Максимум попыток
delayMs = 500, // Базовая задержка
backoff = BackoffStrategy.EXPONENTIAL, // Стратегия увеличения задержки
multiplier = 2.0, // Множитель для экспоненциальной стратегии
maxDelayMs = 5000, // Максимальная задержка
retryOn = {IOException.class}, // На какие исключения повторять
noRetryOn = {IllegalArgumentException.class}, // Какие НЕ повторять
fallbackMethod = "fetchDataFallback" // Fallback при исчерпании попыток
)
public String fetchData(String url) {
// код, который может выбросить исключение
}
public String fetchDataFallback(String url, Throwable ex) {
return "cached-data";
}Стратегии backoff:
FIXED— фиксированная задержкаLINEAR— линейный рост (delay * attempt)EXPONENTIAL— экспоненциальный рост (delay * multiplier^attempt)EXPONENTIAL_WITH_JITTER— экспоненциальный + случайный джиттер
@CircuitBreaker(
name = "external-service", // Уникальное имя (методы с одним именем разделяют состояние)
failureThreshold = 5, // Сбоев для открытия
successThreshold = 3, // Успехов в HALF_OPEN для закрытия
openDurationMs = 30000, // Время в OPEN состоянии
halfOpenRequests = 3, // Пробных запросов в HALF_OPEN
failOn = {RuntimeException.class}, // Что считается сбоем
ignoreExceptions = {ValidationException.class}, // Игнорируемые исключения
fallbackMethod = "getDefault", // Fallback при OPEN
useSlidingWindow = true, // Использовать скользящее окно
slidingWindowSize = 10, // Размер окна
failureRateThreshold = 50 // % ошибок для открытия
)
public String callService() {
// вызов внешнего сервиса
}Состояния Circuit Breaker:
stateDiagram-v2
[*] --> CLOSED
CLOSED --> OPEN : failureThreshold достигнут
OPEN --> HALF_OPEN : openDurationMs истекло
HALF_OPEN --> CLOSED : successThreshold достигнут
HALF_OPEN --> OPEN : сбой при пробном запросе
CLOSED : Нормальная работа
OPEN : Все запросы отклоняются
HALF_OPEN : Пробные запросы
// Вариант 1: явное указание интерфейса
MyService service = new MyServiceImpl();
MyService resilientService = ResilienceProxy.create(service, MyService.class);
// Вариант 2: автоопределение интерфейсов
MyService resilientService = ResilienceProxy.create(service);CircuitBreakerRegistry registry = CircuitBreakerRegistry.getInstance();
// Получить состояние
CircuitBreakerState state = registry.get("external-service");
System.out.println("State: " + state.getState());
System.out.println("Failure rate: " + state.getFailureRate() + "%");
// Сбросить состояние
registry.reset("external-service");
// Сбросить все
registry.resetAll();// Порядок выполнения: CircuitBreaker → Retry → Method
// 1. CB проверяет состояние
// 2. Если CLOSED/HALF_OPEN — выполняется Retry
// 3. Retry делает несколько попыток
// 4. Результат регистрируется в CB
@Retry(maxAttempts = 3, delayMs = 200)
@CircuitBreaker(name = "critical", failureThreshold = 2)
public String criticalOperation() {
// ...
}flowchart LR
Client([Client]) --> Proxy
subgraph Proxy[ResilienceProxy]
subgraph Handler[ResilienceInvocationHandler]
CB[CircuitBreaker Logic] --> Retry[Retry Logic]
CB --> Registry
subgraph Registry[CircuitBreakerRegistry]
S1[CB State 1]
S2[CB State 2]
S3[...]
end
end
end
Retry --> Target([Target Method])
cd java-resilience
mvn compile exec:java -Dexec.mainClass="io.mkalugin.resilience.Main" mvn testMIT