İçeriğe geç

Bulkhead — Tek Yavaş Servis Neden Herkesi Düşürür?

Orta 9 dk Sık karşılaşılır

Önce şunu oku: Circuit Breaker

30 saniyede özet

Bütün dış çağrılar aynı thread havuzunu paylaşırsa, yavaşlayan tek bir bağımlılık thread'leri tutar ve sağlam servislere giden istekler de reddedilir. Her bağımlılığa ayrı bir havuz vermek hasarı o bölmede tutar.

Bir gemide su geçirmez bölmeler vardır. Gövde tek parça olsaydı, tek bir delik bütün gemiyi doldururdu. Bölmeler sayesinde yalnızca delik olan bölme su alır, gemi yüzmeye devam eder.

  1. Bayt: Bakiye, havale ve kart sağlayıcısı aynı uygulamadan çağrılıyor. Basit ve düzenli!

  2. Sen: Kart sağlayıcısı biraz yavaşladı. Şimdi müşteriler bakiyelerini de göremiyor.

  3. Bayt: Ama bakiye servisi sapasağlam! Loglarında tek bir hata yok.

  4. Bayt: Hata bakiye servisinde değil. Bakiyeye gidecek thread kalmamış olabilir.

Hata nasıl yayılır?

Uygulama dış servisleri bir thread poolGörevleri sabit ya da sınırlı sayıda, yeniden kullanılan thread'e dağıtan yapı. Her görev için yeni thread açmanın bellek ve zamanlama maliyetini önler.Sözlükte gör → çağırır. Her çağrı, cevap gelene kadar bir thread’i tutar.

Bir bağımlılık yavaşlarsa onun çağrıları thread’leri uzun süre bırakmaz. Havuz bir süre sonra tamamen yavaş çağrılarla dolar; sağlam servislere giden yeni istekler de thread bulamaz ve reddedilir.

Kafam karıştı, daha basit anlat

Yavaş bir servis thread’leri tutar. Ortak havuz dolunca sağlam servislere de thread kalmaz.

Hızlı kontrolBaşlangıç

Yavaşlayan bir dış servis, hata vermese bile neden başka isteklere zarar verebilir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Bütün dış çağrılar 10 thread'lik tek havuzu paylaşıyor ve kart sağlayıcısı yavaşladı. Bakiye istekleri ne olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Her bağımlılığa bir bölme

Kart sağlayıcısına 4, bakiyeye 3, havaleye 3 thread'lik ayrı havuzlar verdik. Kart sağlayıcısı yavaşladı. Bakiye istekleri ne olur? Cevabı göster

Hiç etkilenmez. Kart havuzu dolar ve kart istekleri reddedilir, ama bakiyenin kendi üç thread’i vardır. Yavaş kart çağrıları o thread’lere dokunamaz.

Bir bölme dolar, gemi yüzmeye devam eder.
Adım adım oku
  1. Gövde tek parça: bir delik.
  2. Su her yere yayıldı, gemi battı.
  3. Araya su geçirmez bölmeler.
  4. Bir bölme dolar, gemi yüzmeye devam eder.

bulkheadHer bağımlılığa ayrı ve sınırlı bir kaynak havuzu vermek. Yavaşlayan bağımlılık kendi havuzunu doldurur, servisin geri kalanı çalışmaya devam eder.Sözlükte gör →, her bağımlılığa ayrı ve sınırlı bir havuz vermektir. Bir bağımlılık yavaşlarsa yalnızca onun bölmesi dolar; hata yayılmaz.

Bölmenin içini de bir zaman aşımı rahatlatır. Yavaş çağrı altı saniye yerine iki saniyede bırakılırsa, kart thread’leri daha çabuk boşalır ve daha çok kart isteği geçer.

Kafam karıştı, daha basit anlat

Her bağımlılığa ayrı havuz: biri dolsa da öbürleri çalışır. Zaman aşımı, dolan havuzun daha çabuk boşalmasını sağlar.

Hızlı kontrolOrta

Bulkhead nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Ayrı havuzlarda kart sağlayıcısı yavaşlarsa ne olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kendin gör

Kart sağlayıcı yavaşladı

Tohum 352667

Oynat ya da adımla · 3. saniyeden sonra kart çağrıları yavaş

Ortak havuz (10 thread)

  • Kart sağlayıcı✓ 0✕ 0 red
  • Bakiye✓ 0✕ 0 red
  • Havale✓ 0✕ 0 red
Hız
Adım 0

Şu an ne oldu?

Herkese tek havuz

Her saniye 9 istek geliyor. 3. saniyeden sonra kart çağrıları 6 saniye sürüyor.

Görevler0/3

  • Sağlam bakiye servisine giden istekler de reddedilsinaçık

    İpucu

    Herkes tek havuzu paylaşsın.

  • Kart istekleri reddedilsin ama bakiye ve havale hiç etkilenmesinaçık

    İpucu

    Her bağımlılığa ayrı bölme.

  • Bakiye ve havale etkilenmesin, reddedilen kart isteği 12 ya da daha az olsunaçık

    İpucu

    Bölme ve zaman aşımı birlikte.

Olay günlüğü (0)

Henüz olay yok. Oynat veya adımla.

  1. Varsayılanla oynat. Üçüncü saniyeden sonra ortak havuz kart çağrılarıyla doldu; bakiye ve havale istekleri de reddedildi.
  2. Zaman aşımını aç. Daha az red var, ama sağlam servislerde hâlâ red görünüyor.
  3. Ayrı havuzları seç, zaman aşımını kapat. Bakiye ve havale hiç etkilenmedi; yalnızca kart istekleri reddedildi.
  4. Zaman aşımını da aç. Bölmeler yerinde, reddedilen kart isteği de azaldı.
Hızlı kontrolOrta

Ortak havuza zaman aşımı eklemek tek başına yeterli midir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Bulkhead havuzunda queue-capacity: 0 ne sağlar?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Tuzaklar

Havuzu çok büyük açmak. Kart havuzuna yüz thread vermek bölmeyi anlamsız kılar; yavaş çağrılar yine bütün belleği ve bağlantıları tüketir. Boyutu gerçek trafikten ölç.

Reddi bir hata gibi göstermek. Dolu bölmeden gelen ret, müşteriye “kart işlemleri şu an yoğun, birazdan tekrar dene” gibi sakin bir cevap olmalı. Aynı ekrandaki bakiye ise çalışmaya devam eder.

Bulkhead’i circuit breaker sanmak. Breaker sürekli hata veren servise istek göndermeyi durdurur; bulkhead ise yavaş bir servisin başkalarını boğmasını engeller. Birbirini tamamlarlar.

Kafam karıştı, daha basit anlat

Havuz boyutunu ölç, reddi sakin bir cevaba çevir, breaker ile bölmeyi birlikte kullan.

Hızlı kontrolOrta

Kart havuzu doluyken müşteriye ne gösterilmeli?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.
Derinleş · Bankacılık API'si: bağımlılık başına bölme 4 dosya · ~78 satır · ilk okumada atlayabilirsin
Proje dosyaları

src/main/resources/ application.yml Yapılandırma: kart sağlayıcısının kendi küçük havuzu ve iki saniyelik zaman aşımı var; bakiye ve havale ayrı bölmelerde.

src/main/resources/application.yml
resilience4j:
thread-pool-bulkhead:
instances:
cardProvider:
core-thread-pool-size: 4
max-thread-pool-size: 4
queue-capacity: 0 # no hidden waiting room: a full pool answers at once
balance:
core-thread-pool-size: 3
max-thread-pool-size: 3
queue-capacity: 0
timelimiter:
instances:
cardProvider:
timeout-duration: 2s

src/main/java/com/bank/cards/ CardProviderClient.java Kart istemcisi: çağrı kendi bölmesinde, dolarsa sakin bir yedek cevap dönüyor.

src/main/java/com/bank/cards/CardProviderClient.java
@Component
class CardProviderClient {
private final RestClient cards;
CardProviderClient(RestClient.Builder builder) {
this.cards = builder.baseUrl("https://card-provider.internal").build();
}
// Runs in its own pool of four threads and gives up after two seconds.
@Bulkhead(name = "cardProvider", type = Bulkhead.Type.THREADPOOL, fallbackMethod = "busy")
@TimeLimiter(name = "cardProvider")
CompletableFuture<CardLimits> limits(String cardId) {
return CompletableFuture.completedFuture(
cards.get().uri("/cards/{id}/limits", cardId).retrieve().body(CardLimits.class));
}
private CompletableFuture<CardLimits> busy(String cardId, Throwable cause) {
return CompletableFuture.completedFuture(CardLimits.temporarilyUnavailable(cardId));
}
}

src/main/java/com/bank/accounts/ BalanceClient.java Bakiye istemcisi: ayrı bölme; kart yavaşlasa da kendi thread'leriyle çalışıyor.

src/main/java/com/bank/accounts/BalanceClient.java
@Component
class BalanceClient {
private final RestClient accounts;
BalanceClient(RestClient.Builder builder) {
this.accounts = builder.baseUrl("https://accounts.internal").build();
}
// A compartment of its own: a slow card provider cannot take these threads.
@Bulkhead(name = "balance", type = Bulkhead.Type.THREADPOOL)
CompletableFuture<Balance> balance(String iban) {
return CompletableFuture.completedFuture(
accounts.get().uri("/accounts/{iban}/balance", iban).retrieve().body(Balance.class));
}
}

src/test/java/com/bank/ BulkheadIsolationTest.java Test: kart bölmesi doluyken bakiye çağrısı hâlâ başarılı.

src/test/java/com/bank/BulkheadIsolationTest.java
class BulkheadIsolationTest {
@Test
void aFullCardCompartmentLeavesBalanceThreadsFree() throws Exception {
ThreadPoolBulkhead card = ThreadPoolBulkhead.of("card",
ThreadPoolBulkheadConfig.custom().coreThreadPoolSize(1).maxThreadPoolSize(1).queueCapacity(0).build());
ThreadPoolBulkhead balance = ThreadPoolBulkhead.of("balance",
ThreadPoolBulkheadConfig.custom().coreThreadPoolSize(1).maxThreadPoolSize(1).queueCapacity(0).build());
CountDownLatch release = new CountDownLatch(1);
card.executeSupplier(() -> { awaitQuietly(release); return "slow"; }); // fills the card compartment
assertThatThrownBy(() -> card.executeSupplier(() -> "second"))
.isInstanceOf(BulkheadFullException.class);
assertThat(balance.executeSupplier(() -> "ok").toCompletableFuture().get()).isEqualTo("ok");
release.countDown();
}
private static void awaitQuietly(CountDownLatch latch) {
try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}

Kendini sına

Şimşek turu1/4

Yavaş bir çağrı, hata vermediği sürece başka isteklere zarar vermez.

Soru 1/3İleri

Bulkhead ile circuit breaker arasındaki fark nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Aklında kalacak üç şey

  1. 1 Ortak bir thread havuzunda yavaş çağrılar thread'leri uzun süre tutar. Havuz dolunca sağlam servislere giden istekler de reddedilir: hata yayılır.
  2. 2 Bulkhead, her bağımlılığa ayrı ve sınırlı bir havuz verir. Bir bağımlılık yavaşlarsa yalnızca onun bölmesi dolar, diğerleri çalışmaya devam eder.
  3. 3 Zaman aşımı thread'leri daha çabuk serbest bırakarak hasarı küçültür; bölme ise hasarın nereye kadar yayılacağını sınırlar. İkisi birlikte kullanılır.
Sonraki kapı Cüzdana 550 TL eklenmeliydi. 7050 TL eklendi. Çağrıların hepsi aynı adrese geldi; hangileri gerçekti? Webhook Almak — Ödeme Tamamlandı Diyen Gerçekten Sağlayıcı mı? · 8 dk

4 kart sonraki derste seni bekliyor

0/4 kart bu dersten toplandı