Reputation: 2042
I understand that we can use the circuit state to fetch the current state with in the context. but is there a way that I can get the circuit state outside the context. for eg in below here If I want to get the circuit state at the controller how do I do that? right now I set up a static variable in the policy but when the circuit is broken it's context is not available to fetch. Here is my current setup.
Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddHttpClient<IIntCall, IntCall>().WrapResilientPolicies();
}
Interface
public interface IIntCall
{
Task<bool> DoSomething();
}
Implementation:
public class IntCall : IIntCall
{
private readonly HttpClient client;
public IntCall(HttpClient httpClient)
{
this.client = httpClient;
}
public async Task<bool> DoSomething()
{
var response = await client.GetAsync("http://www.onegoogle.com");
var content = await response.Content.ReadAsStringAsync();
return false;
}
}
Polly Implementation
public static class CBExtensions
{
public static void WrapResilientPolicies(this IHttpClientBuilder builder)
{
builder.AddPolicyHandler((service, request) =>
GetRetryPolicy().WrapAsync(GetCircuitBreakerPolicy()));
}
private static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{
return HttpPolicyExtensions.HandleTransientHttpError()
.CircuitBreakerAsync(3, TimeSpan.FromSeconds(30), (result, retryAttempt) =>
{
Debug.WriteLine("circuit broken");
},
() =>
{
Debug.WriteLine("circuit closed");
});
}
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions.HandleTransientHttpError()
.Or<Exception>(e => !(e is BrokenCircuitException))
.WaitAndRetryAsync(3,
retryAttempt => TimeSpan.FromMilliseconds(500),
onRetry: (context, attempt) =>
{
Debug.WriteLine("error");
}
);
}
}
Upvotes: 2
Views: 2602
Reputation: 1199
The title of this question is general, without the constraint "without using a static variable". For others that do not have that constraint:
public class SomeHandler
{
private static readonly Polly.CircuitBreaker.AsyncCircuitBreakerPolicy _circuitBreakerPolicy
= Policy.Handle<Exception>().CircuitBreakerAsync(3, TimeSpan.FromMinutes(2));
public async Task Handle(IRequest request)
{
if (_circuitBreakerPolicy.CircuitState == Polly.CircuitBreaker.CircuitState.Open)
{
logger.LogInformation("The '_circuitBreakerPolicy' disallows SomeHandler to handle any requests now.");
return;
}
var wrappedPolicy = Policy.WrapAsync(GetRetryPolicy(), _circuitBreakerPolicy);
await wrappedPolicy.ExecuteAsync(...)
}
...
}
Upvotes: 0
Reputation: 8156
The CircuitState
property is available on the ICircuitBreaker
interface fulfilled by the policy instance: (more in the Polly readme and wiki)
CircuitState state = breaker.CircuitState;
/*
CircuitState.Closed
CircuitState.Open
CircuitState.HalfOpen
CircuitState.Isolated
*/
where breaker
is the policy instance, ie the policy originally returned by your GetCircuitBreakerPolicy()
method.
You could pass that circuit-breaker policy to the Controller via DI. During startup you could do something like:
var breaker = GetCircuitBreakerPolicy();
services.AddSingleton<ICircuitBreakerPolicy<HttpResponseMessage>>(breaker as ICircuitBreakerPolicy<HttpResponseMessage>);
You'd need of course to use that same single instance of breaker
in the call to .AddPolicyHandler(...)
, not manufacture a new/different one.
builder.AddPolicyHandler(GetRetryPolicy().WrapAsync(breaker));
The controller could then receive a copy of the breaker instance by constructor injection:
public class MyController
{
public MyController(ICircuitBreakerPolicy<HttpResponseMessage> breaker, /* etc */)
{
}
}
Passing the circuit-breaker to the controller by registering ICircuitBreakerPolicy<HttpResponseMessage>
on the DI container (as illustrated above) works well if the application is only using a single circuit-breaker instance.
If you have multiple circuit-breaker policy instances which need to be directly accessible elsewhere around the application, eg by controllers, store them in a PolicyRegistry
, and pass the IReadOnlyPolicyRegistry<string>
to the controller by DI. There are .AddPolicyHandlerFromRegistry()
overloads for configuring HttpClient
with policies from a PolicyRegistry
.
Upvotes: 3