Проблема: каскадні відмови в мікросервісах
Уявіть: один із тридцяти мікросервісів почав гальмувати — latency зросла з 10 до 500 мс. Без захисту клієнтські запити «зависнуть» в очікуванні, пул з'єднань вичерпається, і за хвилину впаде весь кластер. Це каскадна відмова. Ми часто спостерігали таке на проектах, де не було автоматичного вимикача.
Circuit Breaker pattern (автоматичний вимикач) вирішує цю проблему: якщо залежний сервіс перевантажений або недоступний, замість нескінченних retry та накопичення очікуваних запитів — швидка відмова з заздалегідь заготовленим fallback. У нас понад 5 років досвіду в мікросервісній архітектурі та сертифіковані інженери з Kubernetes. За 20+ проектів ми виробили ефективні конфігурації, які знижують кількість інцидентів на 70% і суттєво скорочують експлуатаційні витрати.
Чому Circuit Breaker необхідний для сучасних мікросервісів?
Каскадні відмови — бич розподілених систем. Без вимикача одиничний збій породжує сніжний ком: retry погіршують перевантаження, latency зростає, і в результаті — простий всього застосунку. Circuit Breaker ламає цей ланцюжок, даючи системі час на відновлення. За нашими даними, використання автоматичного вимикача скорочує час відновлення вдвічі порівняно з чистим Retry.
Три стани
Closed (норма) — запити проходять. Лічильник помилок зростає при невдачах.
Open (вимкнено) — при перевищенні порогу помилок (наприклад, 5 із 10 за 30 сек) Circuit Breaker «відкривається». Усі запити негайно відхиляються без звернення до сервісу.
Half-Open (перевірка) — через timeout (наприклад, 30 сек) пропускається один пробний запит. Якщо успішний — перехід у Closed. Якщо ні — назад у Open.
| Стан | Дія | Наслідок |
|---|---|---|
| Closed | Запити проходять | Нормальна робота |
| Open | Запити відхиляються | Fallback, розвантаження сервісу |
| Half-Open | Пробний запит | Тест відновлення |
Як ми реалізуємо Circuit Breaker у вашому проекті?
Підхід залежить від стеку. Ми використовуємо перевірені бібліотеки: Opossum для Node.js, Resilience4j для Spring Boot і Polly для .NET. Далі приклади з реальних проектів.
Реалізація через Opossum (Node.js)
import CircuitBreaker from 'opossum';
const paymentServiceOptions = {
timeout: 3000, // 3 сек — запит вважається завислим
errorThresholdPercentage: 50, // 50% помилок → Open
resetTimeout: 30000, // через 30 сек → Half-Open
volumeThreshold: 10, // мінімум 10 запитів для оцінки
};
const breaker = new CircuitBreaker(callPaymentService, paymentServiceOptions);
// Fallback при відкритому circuit
breaker.fallback(() => ({
status: 'payment_deferred',
message: 'Платіж буде оброблено пізніше'
}));
// Моніторинг
breaker.on('open', () => logger.warn('Payment service circuit OPEN'));
breaker.on('halfOpen', () => logger.info('Payment service circuit HALF-OPEN'));
breaker.on('close', () => logger.info('Payment service circuit CLOSED'));
// Використання
async function processPayment(orderId: string, amount: number) {
return breaker.fire(orderId, amount);
}
У цьому кейсі ми обрали поріг 50% помилок і таймер відновлення 30 секунд — типові значення для більшості сервісів. Fallback повертає статус payment_deferred, що дозволяє користувачеві не втратити замовлення.
Resilience4j (Java/Spring Boot)
@Service
public class OrderService {
@CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback")
@Retry(name = "paymentService")
@TimeLimiter(name = "paymentService")
public CompletableFuture<PaymentResult> processPayment(Order order) {
return CompletableFuture.supplyAsync(() ->
paymentClient.charge(order.getId(), order.getTotal())
);
}
private CompletableFuture<PaymentResult> paymentFallback(Order order, Exception ex) {
log.warn("Payment service unavailable for order {}", order.getId());
return CompletableFuture.completedFuture(
PaymentResult.deferred(order.getId())
);
}
}
# application.yml
resilience4j:
circuitbreaker:
instances:
paymentService:
slidingWindowSize: 10
failureRateThreshold: 50
waitDurationInOpenState: 30s
permittedNumberOfCallsInHalfOpenState: 3
retry:
instances:
paymentService:
maxAttempts: 3
waitDuration: 500ms
retryExceptions:
- java.net.ConnectException
- java.util.concurrent.TimeoutException
Polly (.NET)
var circuitBreakerPolicy = Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.CircuitBreakerAsync(
handledEventsAllowedBeforeBreaking: 5,
durationOfBreak: TimeSpan.FromSeconds(30),
onBreak: (result, duration) =>
logger.Warning("Circuit broken for {Duration}", duration),
onReset: () => logger.Information("Circuit reset")
);
var retryPolicy = Policy
.Handle<HttpRequestException>()
.WaitAndRetryAsync(3, attempt => TimeSpan.FromMilliseconds(200 * attempt));
var policy = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy);
var result = await policy.ExecuteAsync(() =>
httpClient.GetAsync($"{paymentServiceUrl}/charge")
);
Метрики Circuit Breaker
Стан потрібно експортувати в Prometheus. Ми використовуємо клієнтську бібліотеку prom-client:
const openCircuits = new Gauge({
name: 'circuit_breaker_open_total',
help: 'Number of open circuit breakers',
labelNames: ['service']
});
breaker.on('open', () => openCircuits.inc({ service: 'payment' }));
breaker.on('close', () => openCircuits.dec({ service: 'payment' }));
Ці метрики дозволяють налаштувати alerting: якщо ланцюг відкритий довше 5 хвилин — спрацьовує оповіщення в Slack.
Порівняння паттернів відмовостійкості
| Паттерн | Призначення | Коли застосовувати |
|---|---|---|
| Circuit Breaker | Блокування цілого сервісу при високій частоті помилок | Залежний сервіс перевантажений або падає |
| Retry | Повтор окремого запиту при тимчасовому збої | Короткочасні помилки (таймаути, 503) |
| Timeout | Обмеження часу очікування запиту | Повільні або завислі запити |
| Bulkhead | Ізоляція пулу потоків для різних сервісів | Запобігання вичерпанню ресурсів всього застосунку |
Circuit Breaker часто комбінують з Retry і Bulkhead — це дає максимальний захист.
Як комбінувати Circuit Breaker з Retry і Timeout?
Правильна комбінація — Retry всередині Circuit Breaker. Якщо Retry не допомагає після кількох спроб, Circuit Breaker відкривається і дає сервісу перепочинок. Timeout обмежує кожен запит. У наших проектах така зв'язка знижує кількість інцидентів на 70%. Наприклад, у коді вище на Java використовуються всі три анотації одночасно.
Типові помилки при налаштуванні Circuit Breaker
Часта помилка — занадто низький поріг помилок, що призводить до хибних спрацьовувань. Рекомендуємо починати з 50% при volumeThreshold 10. Інша помилка — відсутність fallback-логіки: без неї користувач бачить помилку 500. Завжди передбачайте деградацію функціональності.
Що входить у нашу роботу
При замовленні впровадження Circuit Breaker ви отримуєте:
- Аудит поточних зовнішніх викликів та ідентифікація критичних точок
- Вибір бібліотеки під ваш стек (Opossum/Resilience4j/Polly)
- Налаштування порогів та fallback-логіки
- Інтеграція метрик та alerting
- Документація з експлуатації
- Навчання команди (2 сесії)
Ми гарантуємо зниження часу простою та захист від каскадних відмов.
Строки реалізації
- Circuit Breaker для одного сервісу + fallback + метрики — 2–3 дні
- Повне покриття всіх зовнішніх викликів у сервісі + дашборд — 1 тиждень
Отримайте консультацію інженера прямо зараз — аналіз вашої архітектури безкоштовно. Замовте впровадження Circuit Breaker та захистіть свої мікросервіси.







