Modern dağıtık sistemler, birbirine bağlı hizmetlerin karmaşık ağlarıdır. Bir bileşen başarısız olduğunda, bu hata kaskade etme etkisiyle tüm sistemi çökertebilir. Dayanıklılık kalıplarını uygulamak yalnızca hataları yönetmekle ilgili değildir; kaosun üstesinden gelebilecek sistemler tasarlamakla ilgilidir. Bu kılavuz üç kritik sütunu kapsar: devre kesiciler, akıllı yeniden denemeler ve zarif düşüş.
Devre Kesici Kalıbını Anlama
Devre kesici kalıbı elektrik mühendisliğinden ilham alır. Bir hizmet sürekli olarak başarısız olduğunda, devre kesici "atlar" ve belirli bir süre boyunca o hizmete istek göndermeyi durdurur. Bu, çağıran tarafın zaman aşımılarıyla ezilmesini önler ve aşağı akıştaki hizmetin toparlanması için zaman tanır.
Bir devre kesicide üç durum vardır:
- Kapalı: Normal çalışma. İstekler akar. Başarısızlıklar sayılır.
- Açık: Devre atmıştır. Tüm istekler, aşağı akıştaki hizmeti çağırmadan hızlıca başarısız olur.
- Yarı-Açık: Zaman aşımından sonra, devre, hizmetin toparlanıp toparlanmadığını test etmek için sınırlı sayıda isteğe izin verir.
İşte yeniden denemeler için tenacity kütüphanesini ve kesici için basit bir durum makinesini kullanan Python'da kavramsal bir uygulama:
import time
from enum import Enum
class CircuitState(Enum):
CLOSED = "closed"
OPEN = "open"
HALF_OPEN = "half_open"
class CircuitBreaker:
def __init__(self, failure_threshold=5, recovery_timeout=30):
self.failure_count = 0
self.state = CircuitState.CLOSED
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.last_failure_time = None
def call(self, func, *args, **kwargs):
if self.state == CircuitState.OPEN:
if time.time() - self.last_failure_time > self.recovery_timeout:
self.state = CircuitState.HALF_OPEN
else:
raise Exception("Circuit is Open")
try:
result = func(*args, **kwargs)
if self.state == CircuitState.HALF_OPEN:
self.state = CircuitState.CLOSED
self.failure_count = 0
return result
except Exception as e:
self.record_failure()
raise e
def record_failure(self):
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = CircuitState.OPEN
Geri Çekme ile Stratejik Yeniden Denemeler
Yeniden denemeler, ağ kesintileri veya geçici aşırı yük gibi geçici hatalar için gereklidir. Ancak, hemen yeniden denemek, zorlanan bir hizmete daha fazla trafik göndererek sorunu kötüleştirebilir. Her zaman karışıklık (jitter) içeren üstel geri çekme (exponential backoff) kullanın.
Karışıklık (jitter), yeniden denemelerin "gürültü sürüsü" (thundering herd) etkisiyle tam olarak aynı anda hizmete ulaşmasını önlemek için gecikmeyi rastgeleleştirir.
import random
def retry_with_backoff(func, max_retries=3, base_delay=1):
for attempt in range(max_retries):
try:
return func()
except Exception as e:
if attempt == max_retries - 1:
raise e
delay = (2 ** attempt) + random.uniform(0, 1)
time.sleep(delay)
Zarif Düşüş: Önemli Yedek Planlar
Birincil hizmet kullanılamadığında, sistem tamamen başarısız olmak yerine zarifçe düşmelidir. Bu, azaltılmış ancak işlevsel bir deneyim sunan yedek davranışları tanımlamayı içerir.
Zarif düşüş örnekleri şunları içerir:
- Eski bir göstergeyle önbellekteki verileri döndürme.
- Varsayılan veya statik içerik sağlama.
- Çekirdek işlevselliği korurken kritik olmayan özellikleri devre dışı bırakma.
Örneğin, bir öneri motoru başarısız olursa, bir e-ticaret sitesi hata sayfası göstermek yerine yerel önbellekten "Popüler Ürünler"i göstermeye düşebilir.
Sonuç
Dayanıklılık bir tasarım disiplinidir, sonradan düşünülen bir şey değildir. Kaskade hataları durdurmak için devre kesicileri, geçici sorunları ele almak için akıllı yeniden denemeleri ve kullanıcı güvenini korumak için zarif düşüşü birleştirerek yalnızca sağlam değil, aynı zamanda dayanıklı sistemler inşa edersiniz. Hizmetlerinizi enstrümantize ederek başlayın, kritik yollarınızı belirleyin ve bu kalıpları en çok ihtiyaç duyulan yerlerde uygulayın.