Git Product home page Git Product logo

helpful.circuitbreaker's Introduction

Helpful.CircuitBreaker

Nuget

Install-Package Helpful.CircuitBreaker

Quick Start

The following code will initialise a basic circuit breaker which once open will not try to close until 1 minute has passed.

CircuitBreakerConfig config = new CircuitBreakerConfig
{
    BreakerId = "Some unique and constant identifier that indicates the running instance and executing process"
};
CircuitBreaker circuitBreaker = new CircuitBreaker(config);

To inject a circuit breaker into class TargetClass using Ninject, try code similar to this:

Bind<ICircuitBreaker>().ToMethod(c => new CircuitBreaker(new CircuitBreakerConfig
{
    BreakerId = string.Format("{0}-{1}-{2}", "Your breaker name", "TargetClass", Environment.MachineName)
})).WhenInjectedInto(typeof(TargetClass)).InSingletonScope();

The above code will reuse the same breaker for all instances of the given class, so the breaker continues to report state continuously across different threads. When opened by one use, all instances of TargetClass will have an open breaker.

To allow configuration of circuit breakers from your web.config or app.config, refer to the following example.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="circuitBreakerConfiguration" type="Helpful.CircuitBreaker.Config.Sections.CircuitBreakerConfigurationSection, Helpful.CircuitBreaker"/>
  </configSections>
  <circuitBreakerConfiguration>
    <CircuitBreakers>
      <CircuitBreaker BreakerId="TestBreaker1"
                      AppliedToConcreteType="Helpful.CircuitBreaker.Test.Unit.Resources.Dummy1, Helpful.CircuitBreaker.Test.Unit"
                      BreakerOpenPeriods="[30,90,200]"
                      PermittedExceptionBehaviour="Swallow">
        <Exceptions ListType="Whitelist">
          <add ExceptionType="Helpful.CircuitBreaker.Test.Unit.Resources.DummyException1, Helpful.CircuitBreaker.Test.Unit" />
        </Exceptions>
      </CircuitBreaker>
      <CircuitBreaker BreakerId="TestBreaker2"
                      AppliedToConcreteType="Helpful.CircuitBreaker.Test.Unit.Resources.Dummy2, Helpful.CircuitBreaker.Test.Unit"
                      BreakerOpenPeriods="[30]"
                      OpenEventTolerance="3"
                      PermittedExceptionBehaviour="PassThrough">
        <Exceptions ListType="Blacklist">
          <add ExceptionType="Helpful.CircuitBreaker.Test.Unit.Resources.DummyException1, Helpful.CircuitBreaker.Test.Unit" />
          <add ExceptionType="Helpful.CircuitBreaker.Test.Unit.Resources.DummyException2, Helpful.CircuitBreaker.Test.Unit" />
        </Exceptions>
      </CircuitBreaker>
      <CircuitBreaker BreakerId="TestBreaker3"
                      AppliedToConcreteType="Helpful.CircuitBreaker.Test.Unit.Resources.Dummy3, Helpful.CircuitBreaker.Test.Unit"
                      Timeout="200"
                      UseTimeout="true">
      </CircuitBreaker>
    </CircuitBreakers>
  </circuitBreakerConfiguration>
</configuration>

The following code will result in a dictionary mapping the target class for dependency injection to the config for the circuit breaker being injected.

Dictionary<string, CircuitBreakerConfig> breakerConfigs = new Dictionary<string, CircuitBreakerConfig>();
CircuitBreakerConfigurationSection circuitBreakerConfigurationSection = ConfigurationManager.GetSection("circuitBreakerConfiguration") as CircuitBreakerConfigurationSection;
foreach (CircuitBreakerConfigSection config in circuitBreakerConfigurationSection.CircuitBreakers)
{
    breakerConfigs.Add(config.AppliedToConcreteType, config.ToBreakerConfig());
}

The dictionary can now be used to create ioc bindings using your preferred ioc container.

Usage

There are 2 primary ways that the circuit breaker can be used:

  1. Exceptions thrown from the code you wish to break on can trigger the breaker to open.
  2. A returned value from the code you wish to break on can trigger the breaker to open.

Here are some basic examples of each scenario.

In the following example, exceptions thrown from _client.Send(request) will cause the circuit breaker to react based on the injected configuration.

public class MakeProtectedCall
{
    private ICircuitBreaker _breaker;
    private ISomeServiceClient _client;

    public MakeProtectedCall(ICircuitBreaker breaker, ISomeServiceClient client)
    {
        _breaker = breaker;
        _client = client;
    }

    public Response ExecuteCall(Request request)
    {
    	Response response = null;
        _breaker.Execute(() => response = _client.Send(request));
        return response;
    }
}

In the following example, exceptions thrown by _client.Send(request) will still trigger the exception handling logic of the breaker, but the lamda applies additional logic to examine the response and trigger the breaker without ever receiving an exception. This is particularly useful when using an HTTP based client that may return failures as error codes and strings instead of thrown exceptions.

public class MakeProtectedCall
{
    private ICircuitBreaker _breaker;
    private ISomeServiceClient _client;

    public MakeProtectedCall(ICircuitBreaker breaker, ISomeServiceClient client)
    {
        _breaker = breaker;
        _client = client;
    }

    public Response ExecuteCall(Request request)
    {
    	Response response = null;
        _breaker.Execute(() => {
        	response = _client.Send(request));
        	return response.Status == "OK" ? ActionResponse.Good : ActionResult.Failure;
        }
        return response;
    }
}

Tracking Circuit Breaker State

The suggested method for tracking the state of the circuit breaker is to handle the breaker events. These are defined on the CircuitBreaker class as:

/// <summary>
/// Raised when the circuit breaker enters the closed state
/// </summary>
public event EventHandler<CircuitBreakerEventArgs> ClosedCircuitBreaker;

/// <summary>
/// Raised when the circuit breaker enters the opened state
/// </summary>
public event EventHandler<OpenedCircuitBreakerEventArgs> OpenedCircuitBreaker;

/// <summary>
/// Raised when trying to close the circuit breaker
/// </summary>
public event EventHandler<CircuitBreakerEventArgs> TryingToCloseCircuitBreaker;

/// <summary>
/// Raised when the breaker tries to open but remains closed due to tolerance
/// </summary>
public event EventHandler<ToleratedOpenCircuitBreakerEventArgs> ToleratedOpenCircuitBreaker;

/// <summary>
/// Raised when the circuit breaker is disposed
/// </summary>
public event EventHandler<CircuitBreakerEventArgs> UnregisterCircuitBreaker;

/// <summary>
/// Raised when a circuit breaker is first used
/// </summary>
public event EventHandler<CircuitBreakerEventArgs> RegisterCircuitBreaker;

Attach handlers to these events to send information about the event to a logging or monitoring system. In this way, sending state to Zabbix or logging to log4net is trivial.

helpful.circuitbreaker's People

Contributors

rokitsalad avatar simon-taylor-bjss avatar thedobbs avatar

Stargazers

 avatar send2vinnie avatar Alexander avatar Finn Newick avatar Guy Fergusson avatar Weixiao avatar

Watchers

send2vinnie avatar  avatar James Cloos avatar Guy Fergusson avatar  avatar

helpful.circuitbreaker's Issues

License file is missing

Hello,
we would like to use your library, but the license file is missing (the nuget link points to a non-existing file).
Could you please provide a license file? (If you don't care, you could use e.g. the MIT license...)

kind regards
Norbert

Support for async/await actions

Hi,

I have a async Task Foo() method that I want to call from the synchronous CircuitBreaker.Execute method. So far all I try the following unttest code fragment (only works with .NET 4.5.1, http://stackoverflow.com/questions/9343594/how-to-call-asynchronous-method-from-synchronous-method-in-c):

private async Task Foo()
{
throw _thrownException;
}

    protected async override void When()
    {
        try
        {
            _circuitBreaker.Execute(() => { Task t = this.Foo(); t.GetAwaiter().GetResult(); });
        }
        catch (Exception e)
        {
            _caughtException = e;
        }
    }

The unittest will pass. Is there a better solution for this problem ? I believe it would be even better to have a ExecuteAsync method.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.