Короче, решил попробовать в этом году прокачаться, как спикер на девфоруме и в поисках актуальных тем спросил наших техлидов, в чем были основные затыки у ребят и девчат на Level Assessment. И одна из таких штук это Bulkhead паттерн.
Это не паттерн в контексте паттернов разработки, а паттерн в контексте устойчивости к сбоям.
Первое, что меня поразило в этом паттерне, так это то, что bulkhead не переводится, как балка головы. А переводится как “переборка”.
А второе, что это паттерн, который можно применить на двух уровнях:
1️⃣ На архитектурном
2️⃣ И на уровне кода
🏠 На архитектурном уровне
Это значит, что вам нужно располагать зависимость между сервисами по возможности изолированно:
❌ Типа не так:
Service A -> Service B -> Database
✅ А так:
Service A -> Database
Service B -> Database
🧑💻 На уровне кода
Это значит, что вам нужно делать так, чтобы взбесившийся участок кода не выжрал все ресурсы остального приложения. К примеру у вас есть серия запросов к какому-то сервису. И вы хотите их распараллелить.
❌ Если вы сделаете вот так, если вас завалит запросами, то вы исчерпаете пул соединений, если придет куча запросов.
public async Task SendRequestsWithoutBulkhead(HttpClient httpClient, List<HttpRequestMessage> requests)
{
var tasks = new List<Task<HttpResponseMessage>>();
foreach (var request in requests)
{
// Все запросы отправляются параллельно без ограничений
tasks.Add(httpClient.SendAsync(request));
}
await Task.WhenAll(tasks);
}
✅ Вместо этого вы можете ограничить ресурсы с помощью библиотеки Polly:
// Создаём Bulkhead policy с ограничением 10 одновременных запросов и очередью до 20 запросов.
private readonly AsyncBulkheadPolicy<HttpResponseMessage> _bulkheadPolicy = Policy.BulkheadAsync<HttpResponseMessage>(
maxParallelization: 10,
maxQueuingActions: 20,
onBulkheadRejectedAsync: context =>
{
Console.WriteLine("Запрос отклонён из-за перегрузки Bulkhead.");
return Task.CompletedTask;
});
public async Task SendRequestsWithBulkhead(HttpClient httpClient, List<HttpRequestMessage> requests)
{
var tasks = new List<Task<HttpResponseMessage>>();
foreach (var request in requests)
{
// Каждый запрос выполняется через Bulkhead policy,
// что гарантирует, что одновременно будет не более 10 запросов,
// а остальные будут ждать в очереди до 20.
tasks.Add(_bulkheadPolicy.ExecuteAsync(() => httpClient.SendAsync(request)));
}
await Task.WhenAll(tasks);
}
Благодаря Polly это выглядит довльно лайтово. И это круто! Потому что самому писать какую-то ограничивалку может быть писать лень, а тут уже все готово.
🅰️ Итого: Юзайте bulkhead! Изично работает в коде, бахнули Polly и все! Вы спасены на случай, если что-то разложится на молекулы 💘