Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Construindo Sistemas Resilientes com Circuit Breaker

Construindo Sistemas Resilientes com Circuit Breaker

Guilherme Cavalcanti

July 28, 2014
Tweet

More Decks by Guilherme Cavalcanti

Other Decks in Programming

Transcript

  1. ELOQUENT (latim eloquens, -entis) 1. Que sabe (falando) dominar o

    ânimo de quem o ouve 2. [Figurado] Expressivo, convincente, que prova.
  2. – Release It! “A resilient system keeps processing transactions, even

    when there are transient impulses, persistent stresses, or component failures disrupting normal processing.”
  3. Transactions { Não são transações de DB Processo de checkout

    Signup de usuário Comunicação com serviços de terceiros
  4. KEEPS Processing { Falhas em uma transação não afetam outras

    Falhas em serviços não afetam os componentes que não dependem dele
  5. Release It • Michael Nygard — @mtnygard • ResiliênciA •

    Capacidade • Deployment e monitoramento
  6. Circuit Breaker • Cada vez mais comum a necessidade de

    se comunicar via Rede com software que funciona em processos ou máquinas diferentes • Chamadas a serviços externos irão falhar • Quando muitos componentes dependem de um mesmo serviço podemos gerar falhas em cadeia
  7. Inicialização class CircuitBreaker! ! def initialize(options = {})! @timeout =

    options.fetch(:timeout, 0.01)! @failure_threshold = options.fetch(:failure_threshold, 5)! @silent = false! @failure_count = 0! end
  8. #handle • Recebe um bloco com a estratégia de comunicação

    • Baseado no estado do circuito • Tenta executar e captura falhas def handle(&block)! case state! when :closed then try_to_execute &block! when :open then handle_open! end! end
  9. #try to… • Conta quantidade de falhas • Dispara timeout

    • Reseta o contador def try_to_execute(&block)! begin! yield_with_timeout(&block)! reset! rescue Timeout::Error! record_failure! raise $!! end! end! ! def yield_with_timeout(&b)! Timeout::timeout(@timeout, &b)! end! ! def reset! @failure_count = 0! end! ! def record_failure! @failure_count += 1! end!
  10. Uso require "circuit_breaker"! ! circuit = CircuitBreaker.new! conn = Faraday.new!

    ! circuit.handle do! conn.get('http://api.foo.com/me.json')! end! ! circuit.handle do! conn.get('http://api.foo.com/accounts.json')! end
  11. .new class CircuitBreaker! ! def initialize(options = {})! @invocation_timeout =

    options.fetch(:timeout, 0.01)! @failure_threshold = options.fetch(:failure_threshold, 5)! @silent = false! @failure_count = 0! @reset_timeout = 0.1! @last_failure = nil ! end
  12. Mudanças • Tenta executar nos estados closed e half_open •

    Grava o momento da última falha • Limpa última falha no #reset def handle(&b)! case state! when :closed, :half_open! try_to_execute &b! when :open then handle_open! end! end! ! def try_to_execute(&block)! begin! yield_with_timeout(&block)! reset! rescue Timeout::Error! record_failure! raise $!! end! end! ! def yield_with_timeout(&b)! Timeout::timeout(@timeout, &b)! end! ! def reset! @failure_count = 0! @last_failure_time = nil! end! ! def record_failure! @last_failure_time = Time.now! @failure_count += 1! end!
  13. #STate def state! case! when (@failure_count >= @failure_threshold) &&! (Time.now

    - @last_failure_time) > @reset_timeout! :half_open! when (@failure_count >= @failure_threshold)! :open! else! :closed! end! end
  14. Pontos de Melhoria • Utilizar o AAsm ou Statesman para

    gerenciar estados • Adicionar monitoramento através de dependency injection • Circuit breaker distribuido • Detectar outros erros além do timeout