Modern applications rarely operate in isolation. They communicate with external APIs, microservices, payment gateways, authentication providers, and cloud services. While these integrations are essential, they also introduce network failures, transient errors, and service outages.
A resilient HTTP client can recover from temporary failures without impacting users. Polly v9 provides a modern resilience framework for .NET, allowing developers to apply retry, timeout, circuit breaker, and rate-limiting strategies through reusable pipelines.
In this article, you'll learn how to build production-ready HTTP clients using Polly v9 and .NET 11, understand common resilience patterns, and apply best practices for reliable service-to-service communication.
Why HTTP Resilience Matters
External services can fail for many reasons:
Temporary network interruptions
High server load
DNS resolution failures
API rate limiting
Service restarts
Timeouts
Infrastructure failures
Without resilience, even short-lived issues can cause cascading failures throughout an application.
Common Resilience Patterns
Polly supports several resilience strategies.
| Strategy | Purpose |
|---|---|
| Retry | Retry transient failures |
| Timeout | Stop long-running requests |
| Circuit Breaker | Prevent repeated failures |
| Fallback | Return an alternative response |
| Rate Limiter | Limit outgoing requests |
| Hedging | Send backup requests when appropriate |
Each strategy addresses a different type of failure.
Project Setup
Create a Web API.
dotnet new webapi -n PollyDemo
Install the required package.
dotnet add package Microsoft.Extensions.Http.Resilience
This package integrates Polly v9 with HttpClientFactory.
Register an HTTP Client
builder.Services.AddHttpClient("WeatherApi");
Using HttpClientFactory avoids common issues such as socket exhaustion and centralizes HTTP client configuration.
Configure a Retry Policy
Configure a resilience pipeline.
builder.Services.AddHttpClient("WeatherApi")
.AddStandardResilienceHandler(options =>
{
options.Retry.MaxRetryAttempts = 3;
});
Why Retries?
Some failures are temporary. Retrying after a short delay often succeeds without requiring user intervention.
Retries are appropriate for:
HTTP 503 (Service Unavailable)
HTTP 502 (Bad Gateway)
Temporary network failures
Connection resets
Avoid retrying permanent failures such as authentication errors or invalid requests.
Configure Timeouts
Long-running requests can consume resources unnecessarily.
builder.Services.AddHttpClient("WeatherApi")
.AddStandardResilienceHandler(options =>
{
options.TotalRequestTimeout.Timeout =
TimeSpan.FromSeconds(10);
});
Timeouts ensure requests fail predictably instead of waiting indefinitely.
Configure a Circuit Breaker
A circuit breaker prevents repeated calls to an unhealthy service.
builder.Services.AddHttpClient("WeatherApi")
.AddStandardResilienceHandler(options =>
{
options.CircuitBreaker.FailureRatio = 0.5;
options.CircuitBreaker.SamplingDuration =
TimeSpan.FromSeconds(30);
});
When failures exceed the configured threshold, requests fail immediately until the external service begins recovering.
Making HTTP Requests
Inject the client.
public class WeatherService
{
private readonly HttpClient _client;
public WeatherService(
IHttpClientFactory factory)
{
_client = factory.CreateClient("WeatherApi");
}
public async Task<string> GetForecastAsync()
{
return await _client.GetStringAsync("/weather");
}
}
The configured resilience strategies are applied automatically.
Handling Transient Failures
Instead of surrounding every request with manual retry logic, Polly centralizes resilience.
Avoid repetitive code like:
try
{
return await client.GetAsync(url);
}
catch
{
// Retry manually
}
Using resilience pipelines keeps application code clean and consistent.
End-to-End Request Flow
A typical outbound request follows these steps:
Application sends an HTTP request.
Timeout policy starts.
Request reaches the external service.
If a transient failure occurs, Polly retries.
If repeated failures exceed the threshold, the circuit breaker opens.
Successful responses reset failure tracking.
The application receives the final response.
This process improves reliability while protecting both the application and external services.
Retry Strategy Considerations
Retries should be applied selectively.
| Response | Retry? |
|---|---|
| 500 Internal Server Error | Yes |
| 502 Bad Gateway | Yes |
| 503 Service Unavailable | Yes |
| 504 Gateway Timeout | Yes |
| 408 Request Timeout | Yes |
| 400 Bad Request | No |
| 401 Unauthorized | No |
| 403 Forbidden | No |
| 404 Not Found | Usually No |
Retrying permanent failures wastes resources and can increase system load.
Combining Resilience Strategies
A production-ready HTTP client often combines multiple strategies.
Example workflow:
HTTP Request
│
▼
Timeout
│
▼
Retry
│
▼
Circuit Breaker
│
▼
External API
Each strategy addresses a different failure scenario, creating a layered defense against service instability.
Monitoring Resilience
Monitor:
Retry count
Timeout occurrences
Circuit breaker state
Failed requests
Response latency
Success rate
These metrics help identify unstable dependencies before they become major incidents.
Resilience Testing Methodology
The research brief focuses on production resilience but does not include benchmark data or failure simulations. To validate your implementation:
Test Environment
Keep consistent:
.NET SDK version
Network conditions
External API configuration
Hardware resources
Request volume
Test Scenarios
Simulate:
Temporary network failures
HTTP 500 responses
HTTP 503 responses
Slow API responses
Connection timeouts
Complete service outages
Metrics to Collect
Measure:
Successful requests
Failed requests
Retry attempts
Average latency
Timeout count
Circuit breaker activations
Throughput
Useful Tools
Useful tools include:
k6
Apache JMeter
WireMock.Net
dotnet-countersdotnet-trace
Use controlled failure simulations rather than relying on production incidents for validation.
Best Practices
Use
HttpClientFactoryfor all outbound HTTP communication.Retry only transient failures.
Configure reasonable timeout values.
Monitor retry and circuit breaker metrics.
Keep retry counts conservative.
Use circuit breakers to prevent cascading failures.
Log failed requests with sufficient context.
Validate resilience under load and failure conditions.
Common Mistakes
| Mistake | Impact |
|---|---|
| Retrying every failure | Increased load and latency |
| Missing timeout configuration | Requests hang indefinitely |
| Creating HttpClient manually | Socket exhaustion |
| Excessive retry attempts | Amplified service failures |
| Ignoring resilience metrics | Difficult troubleshooting |
| Applying one policy to every endpoint | Inappropriate behavior for different APIs |
Troubleshooting
Requests Never Retry
Verify:
Retry configuration
Response status codes
Exception handling
HTTP client registration
Ensure the failures qualify as transient according to your resilience configuration.
Circuit Breaker Opens Frequently
Investigate:
External service health
Network connectivity
Timeout values
Retry strategy
The circuit breaker is often indicating a genuine downstream issue rather than a problem with Polly itself.
High Request Latency
Review:
Timeout duration
Retry count
External API performance
Network conditions
Excessive retries can increase overall response time if not configured carefully.
FAQs
What is Polly?
Polly is a .NET resilience library that helps applications handle transient faults using strategies such as retries, timeouts, and circuit breakers.
Should every HTTP request be retried?
No. Only transient failures should be retried. Permanent failures, such as authentication or validation errors, generally should not.
Why use HttpClientFactory with Polly?
HttpClientFactory manages HTTP client lifetimes efficiently and allows resilience strategies to be configured centrally for all outgoing requests.
What does a circuit breaker do?
A circuit breaker temporarily stops sending requests to an unhealthy service after repeated failures, giving it time to recover and preventing cascading failures.
Can I combine multiple resilience strategies?
Yes. Production applications often combine retries, timeouts, circuit breakers, and rate limiting to handle different categories of failures effectively.
Conclusion
Reliable HTTP communication is essential for modern distributed applications. Polly v9, together with HttpClientFactory, provides a structured and maintainable way to handle transient faults without scattering retry logic throughout the codebase.
By applying appropriate resilience strategies, monitoring their behavior, and validating them under realistic failure scenarios, you can build .NET applications that remain responsive even when external dependencies experience temporary instability.
Join the conversation! Your thoughts help the community grow.