Fala Galera,
Continuando a série de posts sobre o padrão Circuit Breaker, na primeira parte falamos sobre os conceitos desse padrão, tratamentos de exceções e sua maquina de estado.
Perdeu a primeira parte clique aqui.
Nesta segunda parte da série de posts, vamos falar como podemos implementar o padrão do Circuit Breaker, como vimos esse padrão utiliza uma maquina de estado para controlar as suas operações.
Vamos ao código =]
Show me the code!!!
Para este exemplo, devemos criar toda a estrutura necessária para o nosso Circuit Breaker. Vamos adicionar uma Interface chamada ICircuitBreaker ela será responsável pela exposição dos métodos necessário do circuito.
public interface ICircuitBreaker { CircuitBreakerState State { get; } void Reset(); void Execute(Action action); bool IsClosed { get; } bool IsOpen { get; } }
Depois vamos adicionar nossa máquina de estado que será um enumerador dos estado do circuito.
public enum CircuitBreakerState { Closed, Open, HalfOpen }
Também vamos criar as exceções que serão tratadas pelo o circuito.
public class OpenCircuitException : Exception { public OpenCircuitException(string message) : base(message) { } } public class CircuitBreakerOperationException : Exception { public CircuitBreakerOperationException(string message, Exception innerException) : base(message, innerException) { } }
Agora vem a parte boa, a implementação do nosso circuito. Vamos adicionar uma classe chamada CircuitBreaker ele irá implementar a interface ICircuitBreaker
public class CircuitBreaker : ICircuitBreaker { public event EventHandler StateChanged; private object monitor = new object(); public int Timeout { get; private set; } public int Threshold { get; private set; } public CircuitBreakerState State { get; private set; } private Timer Timer { get; set; } private int FailureCount { get; set; } private Action Action { get; set; } public bool IsClosed { get { return State == CircuitBreakerState.Closed; } } public bool IsOpen { get { return State == CircuitBreakerState.Open; } } public CircuitBreaker(int threshold = 5, int timeout = 60000) { if (threshold <= 0) throw new ArgumentOutOfRangeException($"{threshold} deve ser maior que zero"); if (timeout <= 0) throw new ArgumentOutOfRangeException($"{timeout} deve ser maior que zero"); this.Threshold = threshold; this.Timeout = timeout; this.State = CircuitBreakerState.Closed; this.Timer = new Timer(timeout); this.Timer.Enabled = false; this.Timer.Elapsed += Timer_Elapsed; } public void Execute(Action action) { if (this.State == CircuitBreakerState.Open) { throw new OpenCircuitException("Circuit breaker is currently open"); } lock (monitor) { try { this.Action = action; this.Action(); } catch (Exception ex) { if (this.State == CircuitBreakerState.HalfOpen) { Trip(); } else if (this.FailureCount <= this.Threshold) { this.FailureCount++; //Ativa o Retry if (this.Timer.Enabled == false) this.Timer.Enabled = true; } else if (this.FailureCount >= this.Threshold) { Trip(); } throw new CircuitBreakerOperationException("Operation failed", ex); } if (this.State == CircuitBreakerState.HalfOpen) { Reset(); } if (this.FailureCount > 0) { this.FailureCount--; } } } public void Reset() { if (this.State != CircuitBreakerState.Closed) { Trace.WriteLine($"Circuito Closed"); ChangeState(CircuitBreakerState.Closed); this.Timer.Stop(); } } #region Machine State Handles private void Trip() { if (this.State != CircuitBreakerState.Open) { Trace.WriteLine($"Circuito Aberto"); ChangeState(CircuitBreakerState.Open); } } private void Timer_Elapsed(object sender, ElapsedEventArgs e) { lock (monitor) { try { Trace.WriteLine($"Retry, Execução nº {this.FailureCount}"); Execute(this.Action); Reset(); } catch { if (this.FailureCount > this.Threshold) { Trip(); this.Timer.Elapsed -= Timer_Elapsed; this.Timer.Enabled = false; this.Timer.Stop(); } } } } private void ChangeState(CircuitBreakerState state) { this.State = state; this.OnCircuitBreakerStateChanged(new EventArgs() { }); } private void OnCircuitBreakerStateChanged(EventArgs e) { if (this.StateChanged != null) { StateChanged(this, e); } } #endregion }
Com essa implementação nosso Circuit Breaker está pronto. O mapa do nosso código ficou assim:
Para o nosso testes, utilizei um Console Application com o seguinte código abaixo, note que estou estourando uma exceção de propósito para forçar o circuito em estado aberto.
static void Main(string[] args) { var cb = new CircuitBreaker.CircuitBreaker(5, 3000); try { cb.Execute(() => { throw new Exception(); }); } catch(CircuitBreakerOperationException ex) { Trace.Write(ex); } catch(OpenCircuitException openEx) { Console.Write(cb.IsOpen); } Console.Write(cb.IsClosed); Console.Read(); }
Na janela de saída do Visual Studio temos a seguinte mensagem, conforme imagem abaixo:
E ai o que achou da implementação ? No terceiro e último post da série vamos utilizar componentes que implementam o padrão Circuit Breaker.
Abs e até a próxima
Pingback: Implementando o Circuit Breaker Pattern – Parte 3 - Rafael Cruz