Circuit Breaker: Çöken Servisi Defalarca Çağırmayı Bırak
Bir servis çöktüğünde retry yapmak sorunu çözmez, aksine yükü artırır. Circuit Breaker bu döngüyü keser: belirli bir hata eşiği aşılınca bağlantıyı tamamen kapatır, sisteme nefes aldırır ve kendiliğinden iyileşmesine izin verir.

Circuit Breaker: Çöken Servisi Defalarca Çağırmayı Bırak
Retry Pattern’ı konuştuğumuzda şunu söylemiştik: geçici hatalar için bekleyerek tekrar dene. Timeout’ta ise şunu öğrendik: cevap gelmiyorsa sonsuza kadar bekleme.
Peki servis tamamen çöktüyse?
Retry yapıyorsun, timeout alıyorsun, tekrar retry yapıyorsun. Her deneme boşa gidiyor, sistem kaynaklarını tüketiyor ve çökmüş servise daha fazla yük bindiriyor. Bu döngüyü kırmak için Circuit Breaker devreye girer.
Elektrik Sigortasından İlham
Evdeki elektrik sigortasını düşün. Bir kısa devre olduğunda sigorta tüm evi karanlığa gömmez. Sadece o devreyi keser. Diğer her şey çalışmaya devam eder.
Circuit Breaker yazılımda tam olarak bunu yapar: bir servis belirli sayıda hata verince o servise giden bağlantıyı keser. Sistem diğer işlemlere devam eder.
Üç Durum
Circuit Breaker’ın üç durumu vardır:
Closed (Kapalı): Normal çalışma hali. İstekler geçiyor, hatalar sayılıyor. Eşik aşılmadıkça bu durumda kalır.
Open (Açık): Devre açık, bağlantı kesildi. Servise istek gönderilmiyor. Gelen tüm istekler anında hata alır — timeout beklenmeden.
Half-Open (Yarı Açık): Belirli bir süre sonra “acaba düzeldi mi?” diye test edilir. Birkaç istek geçirilir. Başarılıysa Closed’a döner, hâlâ hata varsa Open’a geri döner.

Kod ile Görelim
Önce state’i tutacak bir yapı oluşturalım:
type CircuitState = "CLOSED" | "OPEN" | "HALF_OPEN";
interface CircuitBreakerState {
state: CircuitState;
failureCount: number;
successCount: number;
lastFailureTime: number;
}
interface CircuitBreakerOptions {
failureThreshold: number; // Kaç hatadan sonra açılsın
recoveryTimeout: number; // Kaç ms sonra test edilsin
successThreshold: number; // Half-open'da kaç başarı lazım
}
Sonra bu state’i yöneten fonksiyonları yazalım:
function createCircuitBreaker(options: CircuitBreakerOptions) {
const state: CircuitBreakerState = {
state: "CLOSED",
failureCount: 0,
successCount: 0,
lastFailureTime: 0,
};
function onSuccess() {
state.failureCount = 0;
if (state.state === "HALF_OPEN") {
state.successCount++;
if (state.successCount >= options.successThreshold) {
state.state = "CLOSED";
console.log("Circuit CLOSED — service recovered");
}
}
}
function onFailure() {
state.failureCount++;
state.lastFailureTime = Date.now();
if (
state.state === "HALF_OPEN" ||
state.failureCount >= options.failureThreshold
) {
state.state = "OPEN";
console.warn("Circuit OPEN — too many failures");
}
}
async function call<T>(fn: () => Promise<T>): Promise<T> {
if (state.state === "OPEN") {
const elapsed = Date.now() - state.lastFailureTime;
if (elapsed < options.recoveryTimeout) {
throw new Error("Circuit is OPEN — request rejected");
}
state.state = "HALF_OPEN";
state.successCount = 0;
}
try {
const result = await fn();
onSuccess();
return result;
} catch (err) {
onFailure();
throw err;
}
}
function getState(): CircuitState {
return state.state;
}
return { call, getState };
}
Kullanımı:
const breaker = createCircuitBreaker({
failureThreshold: 5, // 5 hatadan sonra aç
recoveryTimeout: 30000, // 30 saniye sonra test et
successThreshold: 2, // 2 başarılı istekten sonra kapat
});
async function getUser(userId: string) {
return breaker.call(() =>
fetchWithTimeout(`https://user-service/users/${userId}`, {}, 3000)
);
}
Fallback ile Birlikte Kullan
Circuit açıkken hata fırlatmak yerine bir fallback döndürebilirsin:
async function getUserWithFallback(userId: string) {
try {
return await breaker.call(() =>
fetchWithTimeout(`https://user-service/users/${userId}`, {}, 3000)
);
} catch {
const cached = await cache.get(`user:${userId}`);
if (cached) return cached;
return { id: userId, name: "Bilinmiyor", fallback: true };
}
}
Retry ile Circuit Breaker Birlikte Çalışır
Bu iki pattern birbirinin rakibi değil, tamamlayıcısıdır.
Retry: “Geçici bir sorun olabilir, bekleyip tekrar dene.” Circuit Breaker: “Servis zaten çökmüş, denemeyi bırak.”
Doğru kullanım şöyle görünür:
async function callWithResilience<T>(fn: () => Promise<T>): Promise<T> {
return breaker.call(() =>
retryWithJitter(fn, 3, 500)
);
}
Servis geçici takılıyorsa retry halleder. Tamamen çöktüyse circuit breaker devreye girer ve retry bile denenmez.

Production’da Dikkat Edilecekler
State’i memory’de tutma. Bu implementasyon tek bir instance için çalışır. Birden fazla pod çalışıyorsa her pod’un ayrı circuit state’i olur. Production’da state Redis gibi paylaşımlı bir yerde tutulmalı ya da hazır bir kütüphane kullanılmalıdır.
Her hatayı sayma. 400 Bad Request bir circuit breaker hatası değildir, servis gayet normal çalışıyor demektir. Sadece 500, 503, timeout gibi gerçek servis hatalarını say.
Monitoring ekle. Circuit ne zaman açıldı, ne zaman kapandı, kaç istek reddedildi — bunları mutlaka logla ve alarm kur.
function onFailure() {
state.failureCount++;
state.lastFailureTime = Date.now();
if (
state.state === "HALF_OPEN" ||
state.failureCount >= options.failureThreshold
) {
state.state = "OPEN";
logger.error("Circuit opened", {
failureCount: state.failureCount,
timestamp: new Date().toISOString(),
});
metrics.increment("circuit_breaker.opened");
}
}
Özet
Circuit Breaker çökmüş servise yapılan boşa istekleri keser, sistemi korur.
Üç durumu vardır: Closed (normal), Open (kesildi), Half-Open (test ediliyor).
Retry ile birlikte çalışır — geçici sorun için retry, tam çöküş için circuit breaker.
Fallback ile birleşince kullanıcı hiç hata görmeyebilir.
Production’da state paylaşımlı bir store’da tutulmalıdır.
Summary
This article explains the Circuit Breaker pattern and how it prevents cascading failures in distributed systems. It covers the three states (Closed, Open, Half-Open), a functional TypeScript implementation, fallback strategies, and how Circuit Breaker works alongside Retry and Timeout patterns in production.
İlgili İçerikler
Benzer Yazılar
İlgili yazı
Retry Pattern: Her Hatada Tekrar Denemek Doğru mu?
Bir istek başarısız olduğunda ilk içgüdü "tekrar dene" olmaktadır. Ama naif bir retry mekanizması sistemi kurtarmak yerine çökertebilir. Bu yazıda Retry Pattern'ı neden ve nasıl doğru uygulamanız gerektiğini anlatıyoruz.
İlgili yazı
Timeout Neden Hayati? Cevap Beklemek Bedava Değil
Bir servis cevap vermiyorsa beklemek masum görünür. Ama o bekleme thread'leri tüketir, belleği doldurur ve tüm sistemi dondurabilir. Timeout'u neden ciddiye almanız gerektiğini ve nasıl doğru kurmanız gerektiğini bu yazıda anlatıyoruz.
Daha eski