İçeriğe atla

Circuit Breaker Deseni: Durumlar, Eşikler, Yeniden Denemeler ve Yedek Yanıtlar

Bir circuit breaker zincirleme arızaları nasıl durdurur: kapalı, açık ve yarı açık durumlar, gerçekçi eşikler, zaman aşımı ve yeniden denemeler, iyi yedek yanıtlar.

· Kurucu ve geliştirici

Yayımlandı:

Üç durumlu durum diyagramı. Son 100 çağrıda hata oranı yüzde 50'ye ulaşınca Closed, Open'a geçer. Open, 60 saniyelik beklemeden sonra Half-open'a geçer. Half-open, deneme çağrıları başarılı olursa Closed'a, biri başarısız olursa Open'a döner. Altta bir çağrı yolu vardır.

Bir ödeme sağlayıcısı elli milisaniye yerine on saniyede yanıt vermeye başlar. Ödeme servisiniz onu çağırmaya devam eder, her istek beklerken bir worker'ı tutar ve bir dakika içinde ödeme servisinde, ödeme sağlayıcısına hiç ihtiyaç duymayan istekler dahil, boş worker kalmaz. Ardından ödeme servisini çağıran servisler de zaman aşımına uğramaya başlar. Tek bir yavaş bağımlılık, birkaç ekibi etkileyen bir kesintiye dönüşmüştür.

Bir circuit breaker bu zinciri, açıkça başarısız olan bir bağımlılığı bir süre çağırmayı reddedip onun yerine hemen yanıt vererek durdurur. Bu yazı üç durumu bir durum diyagramıyla, breaker'ın ne zaman açılacağına karar veren eşikleri, zaman aşımları, yeniden denemeler ve bulkhead'lerle nasıl birleştiğini, iyi bir yedek yanıtı neyin oluşturduğunu ve breaker'ın nereye konacağını, yani kodunuza mı yoksa bir service mesh'e mi, ele alıyor.

Arızalar neden zincirleme yayılır

Aritmetik basit ve acımasızdır. 200 worker thread'i olan ve 50 ms'de yanıt veren bir bağımlılığı çağıran bir servis, o yol üzerinden saniyede yaklaşık 4.000 isteği karşılayabilir. Bağımlılık 10 saniyelik bir zaman aşımına kadar yavaşlarsa aynı 200 thread saniyede 20 isteği karşılar. Servisinizde hiçbir şey bozuk değildir; kapasitesi yalnızca beklemekle tükenmiştir.

Yeniden denemeler durumu kötüleştirir. Üç servis katmanının her biri üçer kez yeniden denerse, tek bir kullanıcı isteği en alttaki zorlanan bağımlılığa 27 çağrıya dönüşebilir; hem de onları en az kaldırabileceği anda.

Michael Nygard circuit breaker'ı yazılımı üretime hazır hâle getirmek üzerine 2007'de yazdığı kitabında anlattı; Martin Fowler'ın tanımı ise onu servis tasarımının standart bir parçası yaptı: Bir bağımlılığa yapılan çağrıları sarmalayın, hataları sayın ve bir eşiği aştıklarında bir süre çağırmayı bırakın.

Üç durum

Durum Çağrılara ne olur Geçtiği durum
Kapalı (Closed) Çağrılar geçer; sonuçlar kayan bir pencerede kaydedilir Hata ya da yavaş çağrı oranı eşiği aştığında açık
Açık (Open) Çağrılar bağımlılığa dokunmadan hemen başarısız olur; yedek yanıt verir Bir bekleme süresinden sonra yarı açık
Yarı açık (Half-open) Az sayıda deneme çağrısı geçer; geri kalanı yine hızla başarısız olur Denemeler başarılı olursa kapalı, başarısız olursa açık

Açık durum aynı anda iki iş görür. Servisinizi korur, çünkü hızla başarısız olmak worker'ları anında serbest bırakır. Bağımlılığı da korur, çünkü toparlanırken giderek büyüyen bir yeniden deneme yığını yerine trafikten bir mola alır.

Yarı açık durum vardır, çünkü breaker denemeden bağımlılığın yeniden sağlıklı olduğunu bilemez. Trafiğin tamamını değil, bir avuç çağrıyı geçirmek, bağımlılığı geri döndüğü anda yeniden devirmeden toparlanmayı sınar.

Ne hata sayılır

Buna bilinçli karar verin, çünkü yanlış seçim breaker'ları yanlış nedenlerle açar:

  • Sayın: zaman aşımları, bağlantı hataları, 5xx yanıtları ve yavaş çağrı eşiğinizden yavaş çağrılar.
  • Saymayın: 400 ya da 404 gibi 4xx yanıtları. Bunlar başarısız bir bağımlılığı değil, hatalı bir isteği anlatır ve hatalı davranan tek bir istemci herkesi kesmemelidir.
  • Duruma göre karar verin: 429 Too Many Requests. Bağımlılığın kendini koruduğu anlamına gelir; bu da çoğu zaman geri çekilmek için iyi bir nedendir.

Yavaş çağrılar hatalar kadar önemlidir. Her isteği sekiz saniyede başarıyla yanıtlayan bir bağımlılık, etkileşimli bir API için başarısız bir bağımlılıktır.

Eşikleri seçmek

JVM'de yaygın kullanılan bir breaker kütüphanesi olan Resilience4j, temkinli varsayılanlarla gelir; pek çok ekip bunları hiç değiştirmediği için bilinmeye değerdir:

Ayar Varsayılan Anlamı
failureRateThreshold %50 Kaydedilen çağrıların yarısı başarısız olduğunda aç
slidingWindowType / slidingWindowSize Sayıya dayalı, 100 Son 100 çağrıya göre değerlendir
minimumNumberOfCalls 100 100 çağrı kaydedilene kadar değerlendirme yapma
slowCallDurationThreshold 60.000 ms Bundan yavaş çağrılar yavaş sayılır
slowCallRateThreshold %100 Yalnızca her çağrı yavaşsa aç
waitDurationInOpenState 60.000 ms Bir dakika açık kal
permittedNumberOfCallsInHalfOpenState 10 Toparlanmaya on deneme çağrısı karar verir

Etkileşimli bir API için yavaş çağrı varsayılanları fiilen kapalıdır: Kimse bir yanıt için 60 saniye beklemez. Makul başlangıç noktaları, gecikme hedefinize yakın bir yavaş çağrı eşiği, %50 ile %80 arasında bir yavaş çağrı oranı ve 10 ile 30 saniye arasında bir açık bekleme süresidir. minimumNumberOfCalls değerini anlamlı tutun: Az trafikli bir uç noktada üç çağrıdan ikisinin başarısız olması bir kesinti değil, gürültüdür. Trafik gün içinde çok değişiyorsa zamana dayalı pencereler sayıya dayalı olanlardan daha iyi davranır.

Zaman aşımları, yeniden denemeler ve bulkhead'ler

Circuit breaker, yalnızca birlikte iyi çalışan dört araçtan biridir:

  • Zaman aşımları önce gelir. Zaman aşımı olmadan yavaş bir çağrı asla başarısız olmaz; dolayısıyla breaker sayacak hiçbir şey görmez. Her zaman aşımını çağıranın kendi zaman aşımının altında ayarlayın ve süre sınırlarını çağrı zinciri boyunca aktarın ki kimse zaten terk edilmiş bir istek üzerinde çalışmaya devam etmesin.
  • Yeniden denemelerin sınırı olmalı. Yalnızca idempotent işlemleri yeniden deneyin, AWS Architecture Blog'da anlatıldığı gibi jitter'lı üstel geri çekilme kullanın, her katmanda değil tek bir katmanda yeniden deneyin ve yeniden denemeleri trafiğin bir payıyla sınırlayın. Envoy'un yeniden deneme bütçeleri, etkinleştirdiğinizde varsayılan olarak etkin isteklerin %20'sidir.
  • Açık bir breaker'ı asla yeniden denemeyin. Resilience4j, retry'ı varsayılan olarak breaker'ın dışında uygular; bu yüzden her deneme breaker'dan geçer. Kararını zaten vermiş bir breaker'ı dövmek yerine, yeniden denemeleri “call not permitted” hatasında duracak şekilde yapılandırın.
  • Bulkhead'ler yalıtır. Her bağımlılığa kendi bağlantı havuzunu ya da eşzamanlılık sınırını verin; böylece yavaş bir ödeme sağlayıcısı servisteki her worker'ı değil, yalnızca kendi havuzunu tüketebilir.

Anlamlı yedek yanıtlar

Breaker açıkken bir şeyin yanıt vermesi gerekir. Doğru yedek yanıt, verinin ne için kullanıldığına bağlıdır:

Özellik İyi yedek yanıt Kötü yedek yanıt
Öneriler Popüler ürünler ya da bölümü gizlemek Öneri servisini bir döngüde yeniden denemek
Kullanıcı avatarı Yer tutucu bir görsel Görsel servisi yanıt verene kadar sayfayı bloklamak
Kargo tahmini Bir notla birlikte önbellekteki bir tahmin Uydurulmuş bir teslim tarihi
Fiyat ya da ödeme Net bir hata ve yeniden deneme yolu Tahmin edilmiş bir fiyat ya da sessizce atlanan bir tahsilat

Yedek yanıt ucuz olmalı ve az önce başarısız olan şeye bağımlı olmamalıdır. Dürüst cevap “bu şu an çalışmıyor” ise net bir hatayla hızla başarısız olun; bu yine de bir zaman aşımından çok daha iyidir.

Breaker nereye konmalı

Süreç içi kütüphane Service mesh ya da proxy
Örnekler Resilience4j (Java), Polly (.NET), gobreaker (Go), opossum (Node.js) Envoy circuit breaking ve outlier detection
Gördükleri İstisnalar, iş düzeyi hatalar, yedek yanıtlar Bağlantılar, istekler, yanıt kodları
Yedek yanıtlar Evet, kodunuzda Hayır; çağıran bir hata alır
Ekipler arası tutarlılık Her ekip kendi ayarını yapar Tek bir politika, her yerde uygulanır

Envoy'un modelini anlamak faydalıdır, çünkü ders kitabındakinden farklıdır. Circuit breaker'ları upstream cluster başına eşzamanlılık sınırlarıdır; varsayılan olarak 1.024 bağlantı, 1.024 bekleyen istek, 1.024 etkin istek ve 3 eşzamanlı yeniden deneme. Host bazında hata tespiti ayrı bir özelliktir, outlier detection: Varsayılan olarak art arda beş 5xx yanıtından sonra bir host'u 30 saniyeliğine dışarı atar, tekrarlayan atmalarda daha uzun süre, ve bir cluster'ın %10'undan fazlasını asla dışarı atmaz. Service mesh mimari diyagramı bu proxy'lerin nerede durduğunu gösteriyor.

Pek çok ekip ikisini birden kullanır: tutarlı sınırlar ve kötü host'ların dışlanması için mesh'i, uygulamanın gerçek bir yedek yanıta ihtiyaç duyduğu yerde de bir kütüphaneyi. Deseni popülerleştiren Netflix Hystrix 2018'den beri bakım modundadır; JVM'de olağan yerine geçen Resilience4j'dir.

Gözlemleyin ve test edin

Sessizce açılan bir breaker bir kesintiyi bir muammaya çevirir. Her durum değişikliğini bir log olayı ve bir metrik olarak yayın, bir breaker açık kaldığında alarm verin ve breaker durumlarını bağımlılığın gecikmesiyle aynı panoda gösterin. Sonra test edin: Bir staging ortamında bağımlılığa gecikme ve hata enjekte edin ve breaker'ın açılmasını, yedek yanıtın devreye girmesini ve breaker'ın yeniden kapanmasını izleyin. Gözlemlenebilirlik mimari diyagramı metrik ve izleme tarafını ele alıyor.

Circuit breaker diyagramı

Üç durumlu durum diyagramı. Son 100 çağrıda hata oranı yüzde 50'ye ulaşınca Closed, Open'a geçer. Open, 60 saniyelik beklemeden sonra Half-open'a geçer. Half-open, deneme çağrıları başarılı olursa Closed'a, biri başarısız olursa Open'a döner. Altta bir çağrı yolu vardır.

11 bileşen · 7 bağlantı

Archboard'da aç
Diyagram içeriği
  • CLOSED
  • OPEN
  • HALF-OPEN
  • Checkout service (caller)
  • Payment provider
  • Fallback: clear error, retry later
  • failure rate ≥ 50% of the last 100 calls
  • after a 60 s wait
  • a trial call fails
  • trial calls succeed
  • charge(), 2 s timeout, when closed or half-open
  • when open: no call, fail fast
Durum makinesi olarak bir circuit breaker. Kapalı durum çağrıları geçirir ve hataları ölçer; açık durum hızla başarısız olur ve yedek yanıt sunar; yarı açık durum, bağımlılığın toparlanıp toparlanmadığını birkaç deneme çağrısıyla sınar.

Nasıl okunur:

  • Üç durum. Kapalı normal durumdur, açık hızla başarısız olur, yarı açık toparlanmayı sınar. Nokta başlangıç durumunu gösterir.
  • Geçişler koşullarını taşır: Son 100 çağrıda %50 hata oranı breaker'ı açar, 60 saniyelik bekleme onu yarı açığa taşır, deneme çağrılarının sonucu da onu kapatır ya da yeniden açar.
  • Alttaki çağrı yolu çağıranı, breaker'ın arkasındaki ödeme sağlayıcısını ve breaker açıkken yanıt veren yedek yanıtı gösterir.
  • Markdown bloğu ayarları varsayılan değerleriyle listeler.

Dayanıklılık desenlerini net çizmek

  • Breaker'ı bir durum makinesi olarak çizin. Düz yazı açıklamalar açık ile yarı açık arasındaki farkı bulanıklaştırır; bir diyagram bunu belirgin kılar.
  • Sayıları geçişlerin üzerine yazın. “Hatalarda açılır” tartışmaya davet eder; “son 100 çağrının %50'sinde açılır” incelemeye davet eder.
  • Yedek yanıt yolunu, kullanıcının ne gördüğü dahil gösterin.
  • Zaman aşımlarını ve yeniden denemeleri çağrı yolu diyagramına koyun, böylece inceleyenler yeniden denemeleri katmanlar boyunca çarpabilir.

Durum makinesi gösterimi hakkında daha fazlası için durum diyagramı örneğine bakın. Mikroservis mimari diyagramı breaker'ların servisler arasında nerede durduğunu gösteriyor.

Archboard'da oluşturun

  1. Google ya da GitHub ile giriş yapın ve bir diyagram oluşturun.
  2. UML paketinden üç Durum düğümü ekleyin: Closed, Open ve Half-open. Onları yeşil, kırmızı ve kehribar rengine boyayın.
  3. Başlangıç durumu olarak Temel paketinden küçük bir Elips şekli ekleyin ve onu Closed'a bağlayın.
  4. Geçişleri etiketli kenarlar olarak çizin: hata eşiği, bekleme süresi, deneme başarısı ve deneme başarısızlığı. Her durumun yanına, orada çağrılara ne olduğunu söyleyen bir Temel Metin etiketi ekleyin.
  5. Durum makinesinin altına çağıran için bir Temel Kutu, ödeme sağlayıcısı için C4 Modeli paketinden bir Harici sistem ve yedek yanıt için bir Temel Kutu daha ekleyin; açık durum yolu için kesikli bir kenar kullanın.
  6. Ayarlar ve varsayılanlarıyla Temel paketinden bir Markdown bloğu ekleyin.

Örnek 8 diyagram düğümü kullanıyor ve Free planın sınırları içinde kalıyor.

SSS

Bir circuit breaker'ın üç durumu nedir? Çağrıların geçtiği ve hataların sayıldığı kapalı; çağrıların bir bekleme süresi boyunca hemen başarısız olduğu açık; ve birkaç deneme çağrısının breaker'ın yeniden kapanıp kapanmayacağına ya da yeniden açılacağına karar verdiği yarı açık.

Circuit breaker ile yeniden deneme arasındaki fark nedir? Yeniden deneme, başarısız bir çağrıyı başarılı olması umuduyla tekrarlar. Circuit breaker ise hatalar sıklaştığında çağrıları tamamen durdurur. Birlikte kullanıldıklarında yeniden denemeler sınırlı olmalı ve breaker açıkken durmalıdır.

4xx hataları bir circuit breaker'ı açmalı mı? Genellikle hayır. 400 ya da 404, bağımlılığın başarısız olduğunu değil, isteğin hatalı olduğunu gösterir. Bunun yerine zaman aşımlarını, bağlantı hatalarını, 5xx yanıtlarını ve yavaş çağrıları sayın.

Service mesh kullanıyorsam circuit breaker'a ihtiyacım var mı? Mesh size kod değişikliği olmadan eşzamanlılık sınırları ve kötü host'ların dışlanmasını verir. Önbellekteki bir değer ya da kısıtlı bir özellik gibi gerçek bir yedek yanıt istediğiniz yerde yine süreç içi bir breaker'a ihtiyacınız vardır.

Açık durum için iyi bir bekleme süresi nedir? Zorlanan bir bağımlılığın toparlanmasına yetecek kadar uzun, toparlandıktan sonra kullanıcıların uzun bir kesinti görmeyeceği kadar kısa. Etkileşimli API'ler için on ile otuz saniye yaygın bir başlangıç noktasıdır.

Ücretsiz başlayın — tek kullanıcı, kredi kartı gerekmez.

İlgili yazılar

← Blog'a dön

Bir dakikada canvas açın

Google veya GitHub ile giriş yapın. Tek koltuk ücretsiz; ekip katılınca planı yükseltin.