IConfiguration vs IOptions NET
Synchronous and Asynchronous in .NET Core
Model Binding and Validation in ASP.NET Core
ControllerBase vs Controller in ASP.NET Core
ConfigureServices and Configure methods
IHostedService interface in .NET Core
ASP.NET Core request processing
| Communication Between Microservices | Access Token Pattern | |
๐งฑ Bulkhead Pattern in .NET Core |
The Bulkhead pattern is a design pattern used in software architecture to improve the resilience and fault tolerance of a system. It works by partitioning components or resources into isolated pools or "bulkheads" so that a failure or overload in one area does not cause a cascade of failures throughout the entire system. The name comes from the watertight compartments on a ship's hull, which contain flooding to a single section to prevent the ship from sinking.
A common way to implement the Bulkhead pattern in .NET Core is by using the Polly library. This example shows how to use Polly's BulkheadPolicy to isolate API calls to different downstream services.
Scenario: An e-commerce API needs to fetch product details and user reviews from separate, potentially unreliable microservices. The product details are critical for the user interface, while the reviews are secondary. We use a bulkhead to ensure that a slow or failing review service does not block all available threads and cause the entire API to become unresponsive.
Install the necessary Polly packages for your project.
dotnet add package Polly
dotnet add package Microsoft.Extensions.Http.Polly
Use IHttpClientFactory to apply different Bulkhead policies for different downstream services.
using Polly;
var builder = WebApplication.CreateBuilder(args);
// Bulkhead for the critical Product Service
builder.Services.AddHttpClient("productClient", client =>
{
client.BaseAddress = new Uri("https://product-service.example.com/");
}).AddPolicyHandler(Policy.BulkheadAsync<HttpResponseMessage>(
maxParallelization: 10, // Max 10 concurrent requests
maxQueuingActions: 50, // Queue up to 50 requests
onBulkheadRejectedAsync: context =>
{
// Log the rejection
Console.WriteLine("Request to Product Service was rejected by bulkhead.");
return Task.CompletedTask;
}));
// Bulkhead for the non-critical Review Service
builder.Services.AddHttpClient("reviewClient", client =>
{
client.BaseAddress = new Uri("https://review-service.example.com/");
}).AddPolicyHandler(Policy.BulkheadAsync<HttpResponseMessage>(
maxParallelization: 3, // Only 3 concurrent requests
maxQueuingActions: 10, // Queue up to 10 requests
onBulkheadRejectedAsync: context =>
{
// Log the rejection and return a fallback
Console.WriteLine("Request to Review Service was rejected by bulkhead.");
return Task.CompletedTask;
}));
var app = builder.Build();
// Other app configuration...
app.Run();
Inject IHttpClientFactory into your controller or service and create clients based on the named bulkheads.
public class ProductDetailsController : ControllerBase
{
private readonly IHttpClientFactory _clientFactory;
public ProductDetailsController(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
[HttpGet("product/{id}")]
public async Task<IActionResult> GetProductDetails(int id)
{
// Get the client for the critical Product Service
var productClient = _clientFactory.CreateClient("productClient");
var reviewClient = _clientFactory.CreateClient("reviewClient");
var productTask = productClient.GetAsync($"products/{id}");
var reviewsTask = reviewClient.GetAsync($"reviews/{id}");
// Wait for product details, but don't hold up for reviews
var productResponse = await productTask;
// The reviews task might be rejected, so handle the exception
try
{
var reviewResponse = await reviewsTask;
// Process reviews...
}
catch (Polly.Bulkhead.BulkheadRejectedException)
{
// Handle the case where the reviews request was rejected
// Return product details without reviews
}
return Ok(await productResponse.Content.ReadAsStringAsync());
}
}
| Communication Between Microservices | Access Token Pattern | |