middle
Что такое паттерн Bulkhead?
Bulkhead (переборка) — это паттерн отказоустойчивости, изолирующий ресурсы для разных внешних вызовов, чтобы сбой одного компонента не исчерпал ресурсы для других.
Аналогия из жизни: корабль разделён на водонепроницаемые отсеки (bulkheads). Если один отсек затоплен, другие остаются сухими и корабль не тонет.
Проблема без Bulkhead
Пример
Без Bulkhead:
┌──────────────────────────────────┐
│ Thread Pool (20 потоков) │
│ [customer] [customer] [customer] │ ← все потоки заняты зависшим
│ [customer] ... нет потоков для │ customer-service
│ payment или notification! │
└──────────────────────────────────┘
С Bulkhead:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Customer Pool│ │ Payment Pool │ │ Notif. Pool │
│ (5 потоков) │ │ (10 потоков) │ │ (5 потоков) │
│ [зависли] │ │ [работают] │ │ [работают] │
└──────────────┘ └──────────────┘ └──────────────┘
Два типа Bulkhead
| Критерий | Thread Pool | Semaphore |
|---|---|---|
| Изоляция | Полная (отдельные потоки) | Частичная (общий пул) |
| Overhead | Выше (переключение контекста) | Ниже |
| Таймаут | Можно прервать поток | Нет контроля |
| Подходит для | Медленные вызовы | Быстрые вызовы |
Thread Pool Bulkhead
@Service
public class PaymentService {
@Bulkhead(name = "customerService", type = Bulkhead.Type.THREADPOOL,
fallbackMethod = "getCustomerFallback")
public CompletableFuture<CustomerDto> getCustomer(Long id) {
return CompletableFuture.supplyAsync(() -> customerClient.getCustomer(id));
}
private CompletableFuture<CustomerDto> getCustomerFallback(Long id, Throwable t) {
log.warn("Bulkhead для customer-service полон: {}", t.getMessage());
return CompletableFuture.completedFuture(CustomerDto.unknown(id));
}
}
resilience4j:
thread-pool-bulkhead:
instances:
customerService:
maxThreadPoolSize: 5
coreThreadPoolSize: 3
queueCapacity: 10
keepAliveDuration: 60s
paymentGateway:
maxThreadPoolSize: 10
coreThreadPoolSize: 5
queueCapacity: 20
Semaphore Bulkhead
@Bulkhead(name = "customerService", type = Bulkhead.Type.SEMAPHORE)
public CustomerDto getCustomer(Long id) {
return customerClient.getCustomer(id);
}
resilience4j:
bulkhead:
instances:
customerService:
maxConcurrentCalls: 5
maxWaitDuration: 500ms
Комбинация паттернов отказоустойчивости (порядок имеет значение!)
Пример
Запрос → Bulkhead → CircuitBreaker → Retry → Timeout → Вызов сервиса
На собеседовании: объясните через аналогию с кораблём и покажите, что понимаете разницу между Thread Pool и Semaphore Bulkhead. Ключевой вопрос: почему Bulkhead стоит перед Circuit Breaker в цепочке — потому что он ограничивает количество одновременных вызовов ещё до проверки circuit breaker.