diff --git a/Directory.Packages.props b/Directory.Packages.props index c7f3db4921..55a6bc4908 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -16,6 +16,7 @@ + diff --git a/docs/strategies/circuit-breaker.md b/docs/strategies/circuit-breaker.md index ab9a53043d..ad8a27ec28 100644 --- a/docs/strategies/circuit-breaker.md +++ b/docs/strategies/circuit-breaker.md @@ -505,37 +505,35 @@ await uriToCbMappings[downstream1Uri].ExecuteAsync(CallXYZOnDownstream1, Cancell ✅ DO -Use named and typed `HttpClient`s: +Use named `HttpClient`s and then call `AddResilienceHandler` extension that is included in the [`Microsoft.Extensions.Http.Resilience`](https://www.nuget.org/packages/Microsoft.Extensions.Http.Resilience) package. + ```cs -foreach (string uri in uris) -{ - builder.Services - .AddHttpClient(uri, client => client.BaseAddress = new Uri(uri)) - .AddPolicyHandler(GetCircuitBreaker().AsAsyncPolicy()); -} +services + .AddHttpClient("my-client") + .AddResilienceHandler("circuit-breaker", builder => + { + builder.AddCircuitBreaker(new()); + }) + .SelectPipelineByAuthority(); // This call ensures that circuit breaker is cached by each URL authority +``` + -... -private const string serviceUrl = "https://downstream1.com"; -public Downstream1Client( - IHttpClientFactory namedClientFactory, - ITypedHttpClientFactory typedClientFactory) -{ - var namedClient = namedClientFactory.CreateClient(serviceUrl); - var namedTypedClient = typedClientFactory.CreateClient(namedClient); - ... -} +And then use it: + + +```cs +HttpClient client = httpClientFactory.CreateClient("my-client"); + +await client.GetAsync(new Uri("https://downstream1.com/some-path")); ``` + **Reasoning**: -- The `HttpClient` integrates with Circuit Breaker during startup. +- The `HttpClient` integrates with Circuit Breaker during startup by using the `AddResilienceHandler` extension. - There's no need to call `ExecuteAsync()` directly. The `DelegatingHandler` handles it automatically. - -> [!NOTE] -> The above sample code used the `AsAsyncPolicy()` method to convert the `ResiliencePipeline` to `IAsyncPolicy`. -> It is required because the `AddPolicyHandler()` method anticipates an `IAsyncPolicy` parameter. -> Please be aware that, later an `AddResilienceHandler()` will be introduced in the `Microsoft.Extensions.Http.Resilience` package which is the successor of the `Microsoft.Extensions.Http.Polly`. +- By using the `SelectPipelineByAuthority` extension method, the resilience handler caches and assigns resilience pipeline to each authority (scheme + host + port) that is extracted from HTTP request message. ### Reducing thrown exceptions diff --git a/src/Snippets/Docs/CircuitBreaker.cs b/src/Snippets/Docs/CircuitBreaker.cs index ec454febba..d3d71979e8 100644 --- a/src/Snippets/Docs/CircuitBreaker.cs +++ b/src/Snippets/Docs/CircuitBreaker.cs @@ -1,5 +1,8 @@ -using System.Net; +using System; +using System.Net; using System.Net.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Http.Resilience; using Polly.CircuitBreaker; using Snippets.Docs.Utils; @@ -215,6 +218,33 @@ [new Uri("https://downstreamN.com")] = GetCircuitBreaker() #endregion } + public static async ValueTask Pattern_CircuitPerEndpoint() + { + var services = new ServiceCollection(); + + #region circuit-breaker-pattern-cb-per-endpoint + + services + .AddHttpClient("my-client") + .AddResilienceHandler("circuit-breaker", builder => + { + builder.AddCircuitBreaker(new()); + }) + .SelectPipelineByAuthority(); // This call ensures that circuit breaker is cached by each URL authority + + #endregion + + IHttpClientFactory httpClientFactory = null!; + + #region circuit-breaker-pattern-cb-per-endpoint-usage + + HttpClient client = httpClientFactory.CreateClient("my-client"); + + await client.GetAsync(new Uri("https://downstream1.com/some-path")); + + #endregion + } + private static ValueTask IssueRequest() => ValueTask.FromResult(new HttpResponseMessage()); public static async ValueTask AntiPattern_ReduceThrownExceptions() { diff --git a/src/Snippets/Snippets.csproj b/src/Snippets/Snippets.csproj index 0dbab59080..d6bf4d3ac6 100644 --- a/src/Snippets/Snippets.csproj +++ b/src/Snippets/Snippets.csproj @@ -15,6 +15,7 @@ +