Software Architecture

Dayanıklı API Geçitleri: Devre Kesiciler ve Sınırlamalar

Modern mikroservis mimarilerinde API geçidi, tüm istemci trafiği için tek giriş noktası olarak hizmet verir. Ancak bu merkezi rol, onu potansiyel bir tek arıza noktası haline getirir. Sağlam koruma mekanizmaları olmadan, trafik patlamaları veya aşağı akışlı hizmet kesintileri zincirleme reaksiyonlara yol açarak tüm platformu devre dışı bırakabilir. Bu gönderi, dayanıklı, çok protokollü bir giriş katmanı oluşturmak için devre kesicilerin ve hız sınırlamasının uygulanması stratejilerini ele alır.

Giriş Dayanıklılığının Önemi

Dayanıklılık yalnızca çökmeleri önlemekle ilgili değildir; stres altında öngörülebilir davranışı sürdürmekle ilgilidir. Aşağı akışlı bir hizmet yavaşladığında veya yanıt veremez hale geldiğinde, her istek için zaman aşımını beklemek kaynakları tüketir ve bağlantıları meşgul eder. Dayanıklı bir geçit, bu arızaları algılar ve yükü hızla atarak veya hızlıca başarısız olarak hem kullanıcı deneyimini hem de dahili altyapıyı korur.

Çok protokollü giriş, farklı istemcilerin HTTP, gRPC veya WebSocket protokollerini kullanabilmesi nedeniyle karmaşıklık ekler. Her protokolün bağlantı yönetimi ve hata yayılımı için farklı özellikleri vardır. Geçit, tutarlı koruma politikaları uygulamak için bu farklılıkları normalize etmelidir.

Devre Kesicilerin Uygulanması

Devre kesici, bir sistemin başarısız olmaya mahkûm bir işlemi denemesini önleyen bir tasarım kalıbıdır. Genellikle üç durumda çalışır: Kapalı, Açık ve Yarı-Açık.

  • Kapalı: Varsayılan durum. İstekler geçer. Başarısızlıklar izlenir.
  • Açık: Başarısızlık eşiği karşılandığında tetiklenir. İstekler derhal reddedilir.
  • Yarı-Açık: Bir soğuma süresinin ardından, hizmetin kurtarıp kurtarılmadığını test etmek için sınırlı sayıda istek izin verilir.

Yüksek performanslı geçitler oluşturmak için yaygın olarak kullanılan bir dil olan Go'da devre kesicinin kavramsal bir uygulaması aşağıdadır:

type CircuitBreaker struct {
    mu          sync.RWMutex
    failures    int
    threshold   int
    timeout     time.Duration
    isOpen      bool
    openedAt    time.Time
}

func (cb *CircuitBreaker) AllowRequest() bool {
    cb.mu.RLock()
    defer cb.mu.RUnlock()

    if !cb.isOpen {
        return true
    }

    if time.Since(cb.openedAt) > cb.timeout {
        // Transition to Half-Open
        cb.mu.Lock()
        defer cb.mu.Unlock()
        cb.isOpen = false
        cb.failures = 0
        return true
    }

    return false
}

func (cb *CircuitBreaker) RecordFailure() {
    cb.mu.Lock()
    defer cb.mu.Unlock()

    cb.failures++
    if cb.failures >= cb.threshold && !cb.isOpen {
        cb.isOpen = true
        cb.openedAt = time.Now()
    }
}

Çok protokollü destek için, başarısızlık tanımının tutarlı olduğundan emin olun. gRPC için "Unavailable" durum kodu bir başarısızlığı tetikleyebilirken, HTTP için 503 durumu veya bir zaman aşımı tetikleyici olacaktır.

Hız Sınırlama Stratejileri

Hız sınırlama, bir istemcinin belirli bir zaman dilimi içinde bir API'ye yapabileceği istek sayısını kontrol eder. Bu, tek bir istemcinin kaynakları tekeline almasını önler. Yaygın algoritmalar arasında Token Kova (Token Bucket) ve Sızdıran Kova (Leaky Bucket) bulunur.

Token Kova algoritması, trafik patlamalarına izin vermesi nedeniyle API geçitleri için genellikle tercih edilir. Her istemciye sabit sayıda jetona sahip bir kova tahsis edilir. Jetonlar kovaya sabit bir hızda eklenir. Her istek bir jeton tüketir. Kova boşsa, istek reddedilir.

Çok protokollü giriş için hız sınırlaması uygularken, aşağıdakileri göz önünde bulundurun:

  1. İstemci Başına Sınırlar: İstemcileri tanımlamak için API anahtarları veya IP adresleri kullanın. Uzun ömürlü olan WebSocket bağlantıları, kısa ömürlü HTTP isteklerinden farklı sınırlara sahip olmalıdır.
  2. Küresel Sınırlar: Beklenmedik trafik patlamalarından tüm altyapıyı korumak için küresel bir sınır uygulayın.
  3. Kayan Pencere: Bir istemcinin pencere sıfırlanmadan hemen önce ve hemen sonra istek yaparak istek hızını ikiye katlayabileceği kenar durumlarını önlemek için sabit bir pencere yerine kayan pencere kullanın.

Dağıtık sistemler, birden fazla geçit örneği arasında jeton sayılarını izlemek için Redis gibi merkezi bir durum deposu gerektirir. Bu, isteği hangi örneğin işlediğinden bağımsız olarak hız sınırlarının tutarlı bir şekilde uygulandığından emin olunmasını sağlar.

Maksimum Dayanıklılık İçin Stratejilerin Birleştirilmesi

Devre kesiciler ve hız sınırlama birlikte kullanıldığında en iyi sonucu verir. Hız sınırlama kötüye kullanımı ve aşırı yüklenmeyi önlerken, devre kesiciler aşağı akışlı arızalara karşı koruma sağlar. Bir devre kesici açıldığında, geçit, hizmetin geçici olarak devre dışı olduğunu istemciye bildirmek için "503 Service Unavailable" veya özel bir hata kodu döndürebilir. Bu, istemcinin kendi geri çekilme ve yeniden deneme mantığını uygulamasına olanak tanır.

Ayrıca, farklı hizmetleri veya kiracıları izole etmek için toplu bölmeler (bulkheads) uygulamanızı düşünün. Bir hizmet trafik patlaması yaşarsa, diğer hizmetleri aç bırakacak şekilde tüm bağlantı havuzlarını tüketmemelidir.

Sonuç

Dayanıklı bir API geçidi tasarlamak, güvenilir mikroservis mimarileri oluşturmanın kritik bir yönüdür. Aşağı akışlı arızaları yönetmek için devre kesiciler ve trafiği kontrol etmek için hız sınırlaması uygulayarak, sisteminizin değişken yük koşulları altında kullanılabilir ve performanslı kalmasını sağlayabilirsiniz. Dayanıklılık stratejilerinizin beklenen şekilde davrandığını doğrulamak için her zaman yük altında test edin. Unutmayın, dayanıklılık tek seferlik bir yapılandırma değildir; izleme, ayarlama ve iyileştirme sürecidir.

Share: