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:

Without resilience, even short-lived issues can cause cascading failures throughout an application.

Common Resilience Patterns

Polly supports several resilience strategies.

StrategyPurpose
RetryRetry transient failures
TimeoutStop long-running requests
Circuit BreakerPrevent repeated failures
FallbackReturn an alternative response
Rate LimiterLimit outgoing requests
HedgingSend 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:

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:

  1. Application sends an HTTP request.

  2. Timeout policy starts.

  3. Request reaches the external service.

  4. If a transient failure occurs, Polly retries.

  5. If repeated failures exceed the threshold, the circuit breaker opens.

  6. Successful responses reset failure tracking.

  7. 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.

ResponseRetry?
500 Internal Server ErrorYes
502 Bad GatewayYes
503 Service UnavailableYes
504 Gateway TimeoutYes
408 Request TimeoutYes
400 Bad RequestNo
401 UnauthorizedNo
403 ForbiddenNo
404 Not FoundUsually 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:

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:

Test Scenarios

Simulate:

Metrics to Collect

Measure:

Useful Tools

Useful tools include:

Use controlled failure simulations rather than relying on production incidents for validation.

Best Practices

Common Mistakes

MistakeImpact
Retrying every failureIncreased load and latency
Missing timeout configurationRequests hang indefinitely
Creating HttpClient manuallySocket exhaustion
Excessive retry attemptsAmplified service failures
Ignoring resilience metricsDifficult troubleshooting
Applying one policy to every endpointInappropriate behavior for different APIs

Troubleshooting

Requests Never Retry

Verify:

Ensure the failures qualify as transient according to your resilience configuration.

Circuit Breaker Opens Frequently

Investigate:

The circuit breaker is often indicating a genuine downstream issue rather than a problem with Polly itself.

High Request Latency

Review:

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.