Що таке патерн Bulkhead
Structured Java interview answer with junior, middle, and senior-level explanation.
🟢 Junior Level
Bulkhead (перебірка) — патерн, який ізолює ресурси для різних операцій, щоб збій в одній не впливав на інші.
Аналогія з життя: корабель розділений на відсіки (bulkheads). Якщо один відсек затоне — корабель не потоне цілком.
Без Bulkhead:
Thread Pool [====================]
Усі виклики ділять один пул → один повільний сервіс займає усі потоки
З Bulkhead:
Thread Pool A [========] → Сервіс A
Thread Pool B [========] → Сервіс B
Thread Pool C [========] → Сервіс C
Сервіс A повільний → впливає тільки на Pool A
Коли НЕ використовувати Bulkhead
- Один виклик до всього сервісу — якщо сервіс атомарний, розділення потоків безглузде
- Мало потоків — overhead на управління пулами більше вигоди
- Повністю асинхронна система — backpressure вирішує проблему інакше
🟡 Middle Level
Реалізація з Resilience4j
BulkheadConfig config = BulkheadConfig.custom()
.maxConcurrentCalls(10) // максимум 10 одночасних викликів
.maxWaitDuration(Duration.ofSeconds(1)) // чекати 1 сек якщо full
.build();
// maxWaitDuration — скільки потік чекає, якщо bulkhead повний.
// Після закінчення — BulkheadFullException (не timeout!).
// ThreadPool bulkhead ставить в чергу, Semaphore — блокує або кидає.
Bulkhead bulkhead = Bulkhead.of("backend", config);
Supplier<String> decorated = Bulkhead.decorateSupplier(bulkhead,
() -> backendService.call());
Типи Bulkhead
1. Semaphore:
Ліміт одночасних викликів
Легкий, no thread overhead
2. Thread Pool:
Окремий пул потоків для кожного сервісу
Ізоляція на рівні потоків
Типові помилки
- Занадто малий ліміт:
maxConcurrentCalls = 2 → більшість запитів rejected Потрібно налаштувати за метриками
🔴 Senior Level
Архітектурні Trade-offs
| Thread Pool | Semaphore |
|---|---|
| Повна ізоляція (свої потоки), але overhead на кожен потік | Легше (менше пам’яті), але немає ізоляції потоків — усі в одному thread pool |
| Thread isolation | Немає thread isolation |
| Більше ресурсів | Менше overhead |
Production Experience
resilience4j:
bulkhead:
instances:
userService:
maxConcurrentCalls: 20
paymentService:
maxConcurrentCalls: 10
searchService:
maxConcurrentCalls: 30
Best Practices
✅ Окремий bulkhead на кожен зовнішній сервіс
✅ Ліміти за метриками
✅ Моніторьте rejection rate
✅ Комбінуйте з Circuit Breaker
❌ Один bulkhead на все
❌ Занадто малі ліміти
❌ Без моніторингу
🎯 Шпаргалка для співбесіди
Обов’язково знати:
- Bulkhead ізолює ресурси (thread pool) для різних операцій
- Аналогія: відсіки корабля — один потонув, корабель не потонув
- Два типи: Thread Pool (повна ізоляція, overhead) і Semaphore (легший, немає thread isolation)
- Без Bulkhead один повільний сервіс забирає усі потоки спільного пулу
- maxConcurrentCalls налаштовується за метриками для кожного сервісу окремо
- Комбінуйте з Circuit Breaker для максимального захисту
- НЕ потрібен для одного атомарного виклику, малого числа потоків, повністю асинхронних систем
Часті уточнюючі питання:
- Thread Pool vs Semaphore? Thread Pool = свої потоки, повна ізоляція, більше overhead. Semaphore = блокує або кидає, легший.
- Як налаштувати maxConcurrentCalls? За метриками: спостерігайте нормальне навантаження, поставте з запасом.
- Що буде при занадто малому ліміті? Більшість запитів rejected — BulkheadFullException.
- Навіщо окремий bulkhead на кожен сервіс? Щоб повільний Payment Service не впливав на User Service.
Червоні прапорці (НЕ говорити):
- “Bulkhead = Circuit Breaker” — ні, Bulkhead ізолює ресурси, CB блокує виклики
- “Один bulkhead на все достатньо” — ні, тоді немає ізоляції
- “maxConcurrentCalls = 2 — хороший захист” — ні, більшість запитів будуть rejected
- “Semaphore не потрібен, тільки Thread Pool” — Semaphore легший, підходить для багатьох випадків
Пов’язані теми:
- [[17. Як забезпечити відмовостійкість мікросервісів]]
- [[5. Що таке патерн Circuit Breaker]]
- [[6. Як працює Circuit Breaker і які у нього стани]]
- [[19. Що таке патерн Retry і як його правильно використовувати]]
- [[15. Як організувати комунікацію між мікросервісами]]