prakashrajansakthivel
prakashrajansakthivel

Reputation: 2042

How to get the polly circuit breaker state without executing the requests

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

Answers (2)

SymboLinker
SymboLinker

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

mountain traveller
mountain traveller

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

Related Questions