Padrão de Disjuntor AutomáticoCircuit Breaker pattern

Processe falhas que possam demorar um período de tempo de recuperação variável ao ligar a um serviço ou recurso remoto.Handle faults that might take a variable amount of time to recover from, when connecting to a remote service or resource. Isto pode melhorar a estabilidade e resiliência de uma aplicação.This can improve the stability and resiliency of an application.

Contexto e problemaContext and problem

Num ambiente distribuído, as chamadas para serviços e recursos remotos podem falhar devido a falhas transitórias, como ligações de rede lentas, limites de tempo ou recursos sobrecarregados ou temporariamente disponíveis.In a distributed environment, calls to remote resources and services can fail due to transient faults, such as slow network connections, timeouts, or the resources being overcommitted or temporarily unavailable. Estas falhas normalmente corrigidos automaticamente após um curto período de tempo e uma aplicação na cloud robusta deve ser preparada para processá-las através de uma estratégia como o [padrão de repetição] [-padrão de repetição].These faults typically correct themselves after a short period of time, and a robust cloud application should be prepared to handle them by using a strategy such as the [Retry pattern][retry-pattern].

No entanto, pode também haver situações em que as falhas se devem a eventos imprevistos e podem demorar muito mais tempo a corrigir.However, there can also be situations where faults are due to unanticipated events, and that might take much longer to fix. Estas falhas podem variar em termos de gravidade, de uma perda parcial de conectividade à falha total de um serviço.These faults can range in severity from a partial loss of connectivity to the complete failure of a service. Nestas situações, pode ser inútil para uma aplicação repetir continuamente uma operação com baixas probabilidades de ser bem-sucedida. Em vez disso, a aplicação deverá aceitar rapidamente que a operação falhou e processar esta falha adequadamente.In these situations it might be pointless for an application to continually retry an operation that is unlikely to succeed, and instead the application should quickly accept that the operation has failed and handle this failure accordingly.

Adicionalmente, se um serviço estiver muito ocupado, a falha numa parte de um sistema poderá gerar falhas em cascata.Additionally, if a service is very busy, failure in one part of the system might lead to cascading failures. Por exemplo, uma operação que invoque um serviço poderá ser configurada para implementar um limite de tempo e responder com uma mensagem de falha se o serviço não responder dentro deste período.For example, an operation that invokes a service could be configured to implement a timeout, and reply with a failure message if the service fails to respond within this period. No entanto, esta estratégia poderá fazer com que muitos pedidos em simultâneo para a mesma operação sejam bloqueados até que o limite de tempo expire.However, this strategy could cause many concurrent requests to the same operation to be blocked until the timeout period expires. Estes pedidos bloqueados poderão conter recursos de sistema cruciais, como memória, threads, ligações de base de dados, etc.These blocked requests might hold critical system resources such as memory, threads, database connections, and so on. Consequentemente, estes recursos podem ficar esgotados, originando a falha de outras partes (possivelmente separadas) do sistema que precisam de utilizar os mesmos recursos.Consequently, these resources could become exhausted, causing failure of other possibly unrelated parts of the system that need to use the same resources. Nestas situações, é preferível que a operação falhe imediatamente e tente apenas invocar o serviço se tiver boas probabilidades de sucesso.In these situations, it would be preferable for the operation to fail immediately, and only attempt to invoke the service if it's likely to succeed. Note que a definir um limite de tempo mais curto poderá ajudar a resolver o problema, mas o limite de tempo não deve ser tão curto que faça com que a operação falhe na maioria das vezes, mesmo que o pedido ao serviço acabe por ser bem-sucedido.Note that setting a shorter timeout might help to resolve this problem, but the timeout shouldn't be so short that the operation fails most of the time, even if the request to the service would eventually succeed.

SoluçãoSolution

O padrão do Disjuntor Automático, popularizado por Michael Nygard, no seu livro Release It!, pode impedir uma aplicação de tentar repetidamente executar uma operação com boas probabilidades de falhar.The Circuit Breaker pattern, popularized by Michael Nygard in his book, Release It!, can prevent an application from repeatedly trying to execute an operation that's likely to fail. Permitir que esta continue sem esperar que a falha seja corrigida ou desperdiçar ciclos de CPU enquanto determina que a falha é duradoura.Allowing it to continue without waiting for the fault to be fixed or wasting CPU cycles while it determines that the fault is long lasting. O padrão do Disjuntor Automático também permite que uma aplicação detete se a falha foi resolvida.The Circuit Breaker pattern also enables an application to detect whether the fault has been resolved. Se o problema aparentar estar resolvido, a aplicação pode tentar invocar a operação.If the problem appears to have been fixed, the application can try to invoke the operation.

O objetivo do padrão do Disjuntor Automático é diferente do Padrão de repetição.The purpose of the Circuit Breaker pattern is different than the Retry pattern. O Padrão de repetição permite a uma aplicação repetir uma operação com a expectativa de que esta seja bem-sucedida.The Retry pattern enables an application to retry an operation in the expectation that it'll succeed. O padrão do Disjuntor Automático impede que uma aplicação efetue uma operação que é provável que falhe.The Circuit Breaker pattern prevents an application from performing an operation that is likely to fail. Uma aplicação pode combinar estes dois padrões ao utilizar o padrão de Repetição para invocar uma operação através de um disjuntor automático.An application can combine these two patterns by using the Retry pattern to invoke an operation through a circuit breaker. No entanto, a lógica de repetição deve ser sensível a eventuais exceções devolvidas pelo disjuntor automático e abandonar tentativas de repetição se o disjuntor automático indicar que uma falha não é transitória.However, the retry logic should be sensitive to any exceptions returned by the circuit breaker and abandon retry attempts if the circuit breaker indicates that a fault is not transient.

Um disjuntor automático atua como proxy para operações suscetíveis de falhar.A circuit breaker acts as a proxy for operations that might fail. O proxy deverá monitorizar o número de falhas recentes que ocorreram e utilizar esta informação para decidir se devem permitir que a operação continue ou simplesmente devolver uma exceção imediatamente.The proxy should monitor the number of recent failures that have occurred, and use this information to decide whether to allow the operation to proceed, or simply return an exception immediately.

O proxy pode ser implementado como computador com estado, tendo os seguintes estados que emulam a funcionalidade de um disjuntor automático elétrico:The proxy can be implemented as a state machine with the following states that mimic the functionality of an electrical circuit breaker:

  • Fechado: O pedido da aplicação é encaminhado para a operação.Closed: The request from the application is routed to the operation. O proxy mantém uma contagem do número de falhas recentes e, se a chamada para a operação não for bem-sucedida, o proxy aumenta esta contagem.The proxy maintains a count of the number of recent failures, and if the call to the operation is unsuccessful the proxy increments this count. Se o número de falhas recentes exceder um limiar especificado num determinado período de tempo, o proxy é colocado em estado Aberto.If the number of recent failures exceeds a specified threshold within a given time period, the proxy is placed into the Open state. Nesta altura, o proxy inicia um temporizador de limite de tempo e, quando o mesmo terminar, o proxy é colocado em estado Meio-aberto.At this point the proxy starts a timeout timer, and when this timer expires the proxy is placed into the Half-Open state.

    O objetivo do temporizador de limite de tempo é dar ao sistema algum tempo para corrigir o problema na origem da falha antes de permitir à aplicação que tente a operação novamente.The purpose of the timeout timer is to give the system time to fix the problem that caused the failure before allowing the application to try to perform the operation again.

  • Abra: O pedido da aplicação falha imediatamente e uma exceção é devolvida à aplicação.Open: The request from the application fails immediately and an exception is returned to the application.

  • Meio-aberto: Um número limitado de pedidos da aplicação está autorizado a passar e invocar a operação.Half-Open: A limited number of requests from the application are allowed to pass through and invoke the operation. Se estes pedidos forem bem-sucedidos, assume-se que a falha que estava a causar o problema foi corrigida e o disjuntor automático muda para o estado Fechado (a contagem de falhas é reposta).If these requests are successful, it's assumed that the fault that was previously causing the failure has been fixed and the circuit breaker switches to the Closed state (the failure counter is reset). Se algum pedido falhar, o disjuntor automático assume que a falha ainda está presente, pelo que reverte novamente para o estado Aberto e reinicia o temporizador de tempo limite, dando ao sistema mais tempo para recuperar da falha.If any request fails, the circuit breaker assumes that the fault is still present so it reverts back to the Open state and restarts the timeout timer to give the system a further period of time to recover from the failure.

    O estado Meio-Aberto é útil para impedir um serviço de recuperação de ficar repentinamente sobrecarregado com pedidos.The Half-Open state is useful to prevent a recovering service from suddenly being flooded with requests. À medida que o serviço recupera, este poderá conseguir suportar um volume limitado de pedidos até a recuperação ser concluída, mas enquanto a recuperação se encontra em curso, uma sobrecarga de trabalho pode fazer com que o serviço exceda o tempo limite ou falhe novamente.As a service recovers, it might be able to support a limited volume of requests until the recovery is complete, but while recovery is in progress a flood of work can cause the service to time out or fail again.

Estados do Disjuntor Automático

Na figura, o contador de falhas utilizado pelo estado Fechado é baseado em tempo.In the figure, the failure counter used by the Closed state is time based. É automaticamente reiniciado em intervalos periódicos.It's automatically reset at periodic intervals. Isto ajuda a impedir o disjuntor automático de entrar no estado Aberto se experienciar falhas ocasionais.This helps to prevent the circuit breaker from entering the Open state if it experiences occasional failures. O limiar de falha que coloca o disjuntor automático em estado Aberto só é alcançado após um número especificado de falhas ter ocorrido durante um intervalo especificado.The failure threshold that trips the circuit breaker into the Open state is only reached when a specified number of failures have occurred during a specified interval. O contador utilizado pelo estado Meio-Aberto regista o número de tentativas bem-sucedidas de invocar a operação.The counter used by the Half-Open state records the number of successful attempts to invoke the operation. O disjuntor automático regressa ao estado Fechado após um número especificado de invocações de operação consecutivas ter sito efetuado com êxito.The circuit breaker reverts to the Closed state after a specified number of consecutive operation invocations have been successful. Se alguma invocação falhar, o disjuntor automático entra imediatamente no estado Aberto e a contagem de tentativas bem-sucedidas é reposta da próxima vez que entrar no estado Meio-Aberto.If any invocation fails, the circuit breaker enters the Open state immediately and the success counter will be reset the next time it enters the Half-Open state.

A forma como o sistema recupera é processada de forma externa, possivelmente ao restaurar ou reiniciar um componente falhado ou ao reparar uma ligação de rede.How the system recovers is handled externally, possibly by restoring or restarting a failed component or repairing a network connection.

O padrão do Disjuntor Automático confere estabilidade enquanto o sistema recupera de uma falha e minimiza o impacto no desempenho.The Circuit Breaker pattern provides stability while the system recovers from a failure and minimizes the impact on performance. Pode ajudar a manter o tempo de resposta do sistema ao rejeitar rapidamente um pedido de uma operação com boas probabilidades de falhar, em vez de esperar que o limite de tempo de uma operação seja ultrapassado ou que esta nunca seja devolvida.It can help to maintain the response time of the system by quickly rejecting a request for an operation that's likely to fail, rather than waiting for the operation to time out, or never return. Se o disjuntor automático gerar um evento sempre que muda de estado, esta informação pode ser utilizada para monitorizar o estado de funcionamento da parte do sistema protegida pelo disjuntor automático ou para alertar um administrador quando um disjuntor automático passar para o estado Aberto.If the circuit breaker raises an event each time it changes state, this information can be used to monitor the health of the part of the system protected by the circuit breaker, or to alert an administrator when a circuit breaker trips to the Open state.

O padrão é personalizável e pode ser adaptado segundo o tipo da possível falha.The pattern is customizable and can be adapted according to the type of the possible failure. Por exemplo, pode aplicar um temporizador de tempo limite aumentado a um disjuntor automático.For example, you can apply an increasing timeout timer to a circuit breaker. Pode colocar o disjuntor automático em estado Aberto durante alguns segundos numa fase inicial e, se a falha não tiver sido resolvida, aumentar o tempo limite para uns minutos, e assim por diante.You could place the circuit breaker in the Open state for a few seconds initially, and then if the failure hasn't been resolved increase the timeout to a few minutes, and so on. Em alguns casos, em vez de o estado Aberto devolver uma falha e gerar uma exceção, poderá ser útil regressar a um valor predefinido que seja significativo para a aplicação.In some cases, rather than the Open state returning failure and raising an exception, it could be useful to return a default value that is meaningful to the application.

Problemas e consideraçõesIssues and considerations

Deve considerar os seguintes pontos ao decidir como implementar este padrão:You should consider the following points when deciding how to implement this pattern:

Processamento de exceções.Exception Handling. Uma aplicação a invocar uma operação através de um disjuntor automático tem de estar preparada para processar as exceções colocadas se a operação não estiver disponível.An application invoking an operation through a circuit breaker must be prepared to handle the exceptions raised if the operation is unavailable. A forma como as exceções são processadas depende da aplicação.The way exceptions are handled will be application specific. Por exemplo, uma aplicação pode degradar temporariamente o funcionamento da mesma, invocar uma operação alternativa para tentar efetuar a mesma tarefa, obter os mesmos dados ou reportar a exceção ao utilizador e pedir a este que tente novamente mais tarde.For example, an application could temporarily degrade its functionality, invoke an alternative operation to try to perform the same task or obtain the same data, or report the exception to the user and ask them to try again later.

Tipos de Exceções.Types of Exceptions. Um pedido pode falhar por diversos motivos, alguns dos quais poderão indicar um tipo de falha mais grave do que os outros.A request might fail for many reasons, some of which might indicate a more severe type of failure than others. Por exemplo, um pedido pode falhar porque um serviço remoto falhou e serão necessários vários minutos para recuperar, ou devido a um limite de tempo devido ao facto de o serviço estar temporariamente sobrecarregado.For example, a request might fail because a remote service has crashed and will take several minutes to recover, or because of a timeout due to the service being temporarily overloaded. Um disjuntor automático poderá conseguir examinar os tipos de exceções que ocorrem e ajustar a respetiva estratégia consoante a natureza dessas exceções.A circuit breaker might be able to examine the types of exceptions that occur and adjust its strategy depending on the nature of these exceptions. Por exemplo, poderá precisar de um número maior de exceções ao limite de tempo para colocar o disjuntor automático no estado Aberto, relativamente ao número de falhas devido ao sistema se encontrar totalmente indisponível.For example, it might require a larger number of timeout exceptions to trip the circuit breaker to the Open state compared to the number of failures due to the service being completely unavailable.

Registo.Logging. Um disjuntor automático deverá registar todos os pedidos falhados (e, possivelmente, pedidos bem-sucedidos) para permitir que um administrador monitorize o estado de uma operação.A circuit breaker should log all failed requests (and possibly successful requests) to enable an administrator to monitor the health of the operation.

Recuperabilidade.Recoverability. Deverá configurar o disjuntor automático de forma a corresponder ao padrão de recuperação provável da operação que está a proteger.You should configure the circuit breaker to match the likely recovery pattern of the operation it's protecting. Por exemplo, se o disjuntor automático permanecer muito tempo no estado Aberto, poderá colocar exceções, mesmo que o motivo da falha tenha sido resolvido.For example, if the circuit breaker remains in the Open state for a long period, it could raise exceptions even if the reason for the failure has been resolved. De forma semelhante, um disjuntor automático pode aumentar e reduzir os tempos de respostas de aplicações se mudar demasiado rapidamente do estado Aberto para o estado Meio-Aberto.Similarly, a circuit breaker could fluctuate and reduce the response times of applications if it switches from the Open state to the Half-Open state too quickly.

Testar Operações com Falhas.Testing Failed Operations. No estado Aberto, em vez de utilizar um temporizador para determinar quando mudar para o estado Meio-Aberto, um disjuntor automático pode enviar periodicamente um ping ao serviço ou recurso remoto para determinar se voltou a ficar disponível.In the Open state, rather than using a timer to determine when to switch to the Half-Open state, a circuit breaker can instead periodically ping the remote service or resource to determine whether it's become available again. Este ping pode assumir a forma de uma tentativa de invocar uma operação que tinha anteriormente falhado, ou poderá utilizar uma operação especial fornecida pelo serviço remoto especificamente para testar o estado de funcionamento do serviço, conforme descrito pelo Padrão de Monitorização do Estado de Pontos Finais.This ping could take the form of an attempt to invoke an operation that had previously failed, or it could use a special operation provided by the remote service specifically for testing the health of the service, as described by the Health Endpoint Monitoring pattern.

Substituição Manual.Manual Override. Num sistema em que o tempo de recuperação para uma operação falhada é extremamente variável, é benéfico fornecer uma opção de reposição que permita a um administrador fechar um disjuntor automático (e repor a contagem de falhas).In a system where the recovery time for a failing operation is extremely variable, it's beneficial to provide a manual reset option that enables an administrator to close a circuit breaker (and reset the failure counter). De forma semelhante, um administrador pode forçar um disjuntor automático a entrar em estado Aberto (e reiniciar o temporizador de limite de tempo) se a operação protegida pelo disjuntor automático estiver temporariamente indisponível.Similarly, an administrator could force a circuit breaker into the Open state (and restart the timeout timer) if the operation protected by the circuit breaker is temporarily unavailable.

Simultaneidade.Concurrency. O mesmo disjuntor automático poderá ser acedido por um grande número de instâncias simultâneas de uma aplicação.The same circuit breaker could be accessed by a large number of concurrent instances of an application. A implementação não deve bloquear pedidos simultâneos ou adicionar uma carga excessiva a cada chamada a uma operação.The implementation shouldn't block concurrent requests or add excessive overhead to each call to an operation.

Diferenciação de Recursos.Resource Differentiation. Tenha cuidado ao utilizar um único disjuntor automático para um tipo de recurso se puder haver múltiplos fornecedores independentes subjacentes.Be careful when using a single circuit breaker for one type of resource if there might be multiple underlying independent providers. Por exemplo, num arquivo de dados que contenha diversas partições, uma partição poderá estar totalmente acessível, ficando a outra com um problema temporário.For example, in a data store that contains multiple shards, one shard might be fully accessible while another is experiencing a temporary issue. Se as respostas de erro nesses cenários forem unidas, uma aplicação poderá tentar aceder a algumas partições mesmo quando uma falha for provável, enquanto que o acesso a outras partições poderá estar bloqueado, não obstante as boas probabilidades de sucesso.If the error responses in these scenarios are merged, an application might try to access some shards even when failure is highly likely, while access to other shards might be blocked even though it's likely to succeed.

Disjunção Automática Acelerada.Accelerated Circuit Breaking. Por vezes, uma resposta a uma falha pode conter informações suficientes para que o disjuntor automático seja ativado automaticamente e mantenha-se ativado durante um tempo mínimo.Sometimes a failure response can contain enough information for the circuit breaker to trip immediately and stay tripped for a minimum amount of time. Por exemplo, a resposta de erro de um recurso partilhado que está sobrecarregado poderá indicar que uma nova tentativa imediata não é recomendada e que a aplicação deverá tentar novamente em poucos minutos.For example, the error response from a shared resource that's overloaded could indicate that an immediate retry isn't recommended and that the application should instead try again in a few minutes.

Nota

Um serviço pode devolver o erro HTTP 429 (Demasiados Pedidos) se estiver a limitar o cliente ou HTTP 503 (Serviço Indisponível) se o serviço não estiver atualmente disponível.A service can return HTTP 429 (Too Many Requests) if it is throttling the client, or HTTP 503 (Service Unavailable) if the service is not currently available. A resposta pode incluir informações adicionais, como a duração prevista do atraso.The response can include additional information, such as the anticipated duration of the delay.

Repetir Pedidos Falhados.Replaying Failed Requests. No estado Aberto, em vez de simplesmente falhar com demasiada rapidez, um disjuntor automático pode também registar os detalhes de cada pedido num diário e fazer com que estes pedidos sejam repetidos quando o recurso ou serviço remoto ficar disponível.In the Open state, rather than simply failing quickly, a circuit breaker could also record the details of each request to a journal and arrange for these requests to be replayed when the remote resource or service becomes available.

Tempos Limite Inapropriados em Serviços Externos.Inappropriate Timeouts on External Services. Um disjuntor automático poderá não conseguir proteger totalmente as aplicações de operações que falhem em serviços externos configurados com um período de tempo limite bastante prolongado.A circuit breaker might not be able to fully protect applications from operations that fail in external services that are configured with a lengthy timeout period. Se o tempo limite for demasiado longo, executar um disjuntor automático poderá ser bloqueado durante um período alargado antes de o disjuntor automático indicar que a operação falhou.If the timeout is too long, a thread running a circuit breaker might be blocked for an extended period before the circuit breaker indicates that the operation has failed. Durante este período, muitas outras instâncias de aplicações poderão também tentar invocar o serviço através do disjuntor automático e ligar um número significativo de threads antes de todas falharem.In this time, many other application instances might also try to invoke the service through the circuit breaker and tie up a significant number of threads before they all fail.

Quando utilizar este padrãoWhen to use this pattern

Utilize este padrão:Use this pattern:

  • Para impedir uma aplicação de tentar invocar um serviço remoto ou aceder a um recurso partilhado se esta operação tiver fortes probabilidades de falhar.To prevent an application from trying to invoke a remote service or access a shared resource if this operation is highly likely to fail.

Este padrão não é recomendado:This pattern isn't recommended:

  • Para gerir o acesso a recursos privados locais numa aplicação, como a estrutura de dados na memória.For handling access to local private resources in an application, such as in-memory data structure. Neste ambiente, utilizar um disjuntor automático adicionaria uma sobrecarga ao seu sistema.In this environment, using a circuit breaker would add overhead to your system.
  • Como substituto para processar exceções na lógica de negócio das suas aplicações.As a substitute for handling exceptions in the business logic of your applications.

ExemploExample

Numa aplicação Web, várias páginas são preenchidas com dados obtidos de um serviço externo.In a web application, several of the pages are populated with data retrieved from an external service. Se o sistema implementar a cache mínima, a maioria dos resultados para estas páginas causará uma ida e volta ao serviço.If the system implements minimal caching, most hits to these pages will cause a round trip to the service. As ligações da aplicação Web ao serviço poderão ser configuradas com um período de tempo limite (geralmente de 60 segundos) e, se o serviço não responder neste tempo, a lógica em cada página Web irá assumir que o serviço está indisponível e lançar uma exceção.Connections from the web application to the service could be configured with a timeout period (typically 60 seconds), and if the service doesn't respond in this time the logic in each web page will assume that the service is unavailable and throw an exception.

No entanto, se o serviço falhar e o sistema estiver muito ocupado, os utilizadores poderão ser forçados a aguardar até 60 segundos antes de uma exceção ocorrer.However, if the service fails and the system is very busy, users could be forced to wait for up to 60 seconds before an exception occurs. Eventualmente, recursos como memória, ligações e threads poderão ficar esgotados, impedindo outros utilizadores de se ligar ao sistema, mesmo que não estejam a aceder a páginas que obtenham dados do serviço.Eventually resources such as memory, connections, and threads could be exhausted, preventing other users from connecting to the system, even if they aren't accessing pages that retrieve data from the service.

Dimensionar o sistema ao adicionar mais servidores Web e implementar balanceamento de carga poderá atrasar a altura em que os recursos se esgotam, mas não resolverá o problema, uma vez que os pedidos de utilizadores continuarão a não ser responsivos e todos os servidores Web poderão mesmo assim ficar sem recursos.Scaling the system by adding further web servers and implementing load balancing might delay when resources become exhausted, but it won't resolve the issue because user requests will still be unresponsive and all web servers could still eventually run out of resources.

Encapsular a lógica que liga ao serviço e obtém os dados num disjuntor automático pode ajudar a resolver o problema e processar a falha de serviço de forma mais elegante.Wrapping the logic that connects to the service and retrieves the data in a circuit breaker could help to solve this problem and handle the service failure more elegantly. Os pedidos de utilizadores continuarão a falhar, mas irão falhar mais rapidamente e os recursos não serão bloqueados.User requests will still fail, but they'll fail more quickly and the resources won't be blocked.

A classe CircuitBreaker mantém as informações de estado relativas a um disjuntor automático num objeto que implementa a interface ICircuitBreakerStateStore mostrada no seguinte código.The CircuitBreaker class maintains state information about a circuit breaker in an object that implements the ICircuitBreakerStateStore interface shown in the following code.

interface ICircuitBreakerStateStore
{
  CircuitBreakerStateEnum State { get; }

  Exception LastException { get; }

  DateTime LastStateChangedDateUtc { get; }

  void Trip(Exception ex);

  void Reset();

  void HalfOpen();

  bool IsClosed { get; }
}

A propriedade State indica o estado atual do disjuntor automático, que será Open, HalfOpen ou Closed, conforme definido pela enumeração CircuitBreakerStateEnum.The State property indicates the current state of the circuit breaker, and will be either Open, HalfOpen, or Closed as defined by the CircuitBreakerStateEnum enumeration. A propriedade IsClosed deverá ser verdadeira se o disjuntor automático estiver fechado, mas falsa se estiver aberto ou meio aberto.The IsClosed property should be true if the circuit breaker is closed, but false if it's open or half open. O método Trip muda o estado do disjuntor automático para o estado aberto e regista a exceção que causou a mudança de estado, juntamente com a data e a hora em que a exceção ocorreu.The Trip method switches the state of the circuit breaker to the open state and records the exception that caused the change in state, together with the date and time that the exception occurred. As propriedades LastException e LastStateChangedDateUtc devolvem esta informação.The LastException and the LastStateChangedDateUtc properties return this information. O método Reset fecha o disjuntor automático e o método HalfOpen define o disjuntor automático como meio aberto.The Reset method closes the circuit breaker, and the HalfOpen method sets the circuit breaker to half open.

A classe InMemoryCircuitBreakerStateStore no exemplo contém uma implementação da interface ICircuitBreakerStateStore.The InMemoryCircuitBreakerStateStore class in the example contains an implementation of the ICircuitBreakerStateStore interface. A classe CircuitBreaker cria uma instância desta classe para suspender o estado do disjuntor automático.The CircuitBreaker class creates an instance of this class to hold the state of the circuit breaker.

O método ExecuteAction na classe CircuitBreaker encapsula uma operação, especificada como um delegado Action.The ExecuteAction method in the CircuitBreaker class wraps an operation, specified as an Action delegate. Se o disjuntor automático estiver fechado, ExecuteAction invoca o delegado Action.If the circuit breaker is closed, ExecuteAction invokes the Action delegate. Se a operação falhar, um processador de exceção chama TrackException, que define o estado do disjuntor automático como aberto.If the operation fails, an exception handler calls TrackException, which sets the circuit breaker state to open. O seguinte exemplo de código realça este fluxo.The following code example highlights this flow.

public class CircuitBreaker
{
  private readonly ICircuitBreakerStateStore stateStore =
    CircuitBreakerStateStoreFactory.GetCircuitBreakerStateStore();

  private readonly object halfOpenSyncObject = new object ();
  ...
  public bool IsClosed { get { return stateStore.IsClosed; } }

  public bool IsOpen { get { return !IsClosed; } }

  public void ExecuteAction(Action action)
  {
    ...
    if (IsOpen)
    {
      // The circuit breaker is Open.
      ... (see code sample below for details)
    }

    // The circuit breaker is Closed, execute the action.
    try
    {
      action();
    }
    catch (Exception ex)
    {
      // If an exception still occurs here, simply
      // retrip the breaker immediately.
      this.TrackException(ex);

      // Throw the exception so that the caller can tell
      // the type of exception that was thrown.
      throw;
    }
  }

  private void TrackException(Exception ex)
  {
    // For simplicity in this example, open the circuit breaker on the first exception.
    // In reality this would be more complex. A certain type of exception, such as one
    // that indicates a service is offline, might trip the circuit breaker immediately.
    // Alternatively it might count exceptions locally or across multiple instances and
    // use this value over time, or the exception/success ratio based on the exception
    // types, to open the circuit breaker.
    this.stateStore.Trip(ex);
  }
}

O seguinte exemplo mostra o código (omitido do exemplo anterior) que é executado se o disjuntor automático não estiver fechado.The following example shows the code (omitted from the previous example) that is executed if the circuit breaker isn't closed. Começa por verificar se o disjuntor automático esteve aberto durante mais tempo do que o especificado pelo campo local OpenToHalfOpenWaitTime na classe CircuitBreaker.It first checks if the circuit breaker has been open for a period longer than the time specified by the local OpenToHalfOpenWaitTime field in the CircuitBreaker class. Se for este o caso, o método ExecuteAction define o disjuntor automático como meio aberto e, em seguida, tenta efetuar a operação especificada pelo delegado Action.If this is the case, the ExecuteAction method sets the circuit breaker to half open, then tries to perform the operation specified by the Action delegate.

Se a operação for bem-sucedida, o disjuntor automático volta ao estado fechado.If the operation is successful, the circuit breaker is reset to the closed state. Se a operação falhar, volta ao estado aberto e a hora da exceção ocorrida é atualizada, para que o disjuntor automático aguarde mais tempo antes de tentar voltar a efetuar a operação.If the operation fails, it is tripped back to the open state and the time the exception occurred is updated so that the circuit breaker will wait for a further period before trying to perform the operation again.

Se o disjuntor automático estiver aberto durante pouco tempo, menos do que o valor OpenToHalfOpenWaitTime, o método ExecuteAction lança simplesmente uma exceção CircuitBreakerOpenException e devolve o erro que fez com que o disjuntor automático mudasse para o estado aberto.If the circuit breaker has only been open for a short time, less than the OpenToHalfOpenWaitTime value, the ExecuteAction method simply throws a CircuitBreakerOpenException exception and returns the error that caused the circuit breaker to transition to the open state.

Adicionalmente, utiliza um bloqueio para impedir o disjuntor automático de tentar efetuar chamadas simultâneas à operação enquanto está meio aberto.Additionally, it uses a lock to prevent the circuit breaker from trying to perform concurrent calls to the operation while it's half open. Uma tentativa simultânea de invocar a operação será processada como se o disjuntor automático estivesse aberto e irá falhar com uma exceção, como será depois descrito.A concurrent attempt to invoke the operation will be handled as if the circuit breaker was open, and it'll fail with an exception as described later.

    ...
    if (IsOpen)
    {
      // The circuit breaker is Open. Check if the Open timeout has expired.
      // If it has, set the state to HalfOpen. Another approach might be to
      // check for the HalfOpen state that had be set by some other operation.
      if (stateStore.LastStateChangedDateUtc + OpenToHalfOpenWaitTime < DateTime.UtcNow)
      {
        // The Open timeout has expired. Allow one operation to execute. Note that, in
        // this example, the circuit breaker is set to HalfOpen after being
        // in the Open state for some period of time. An alternative would be to set
        // this using some other approach such as a timer, test method, manually, and
        // so on, and check the state here to determine how to handle execution
        // of the action.
        // Limit the number of threads to be executed when the breaker is HalfOpen.
        // An alternative would be to use a more complex approach to determine which
        // threads or how many are allowed to execute, or to execute a simple test
        // method instead.
        bool lockTaken = false;
        try
        {
          Monitor.TryEnter(halfOpenSyncObject, ref lockTaken);
          if (lockTaken)
          {
            // Set the circuit breaker state to HalfOpen.
            stateStore.HalfOpen();

            // Attempt the operation.
            action();

            // If this action succeeds, reset the state and allow other operations.
            // In reality, instead of immediately returning to the Closed state, a counter
            // here would record the number of successful operations and return the
            // circuit breaker to the Closed state only after a specified number succeed.
            this.stateStore.Reset();
            return;
          }
        }
        catch (Exception ex)
        {
          // If there's still an exception, trip the breaker again immediately.
          this.stateStore.Trip(ex);

          // Throw the exception so that the caller knows which exception occurred.
          throw;
        }
        finally
        {
          if (lockTaken)
          {
            Monitor.Exit(halfOpenSyncObject);
          }
        }
      }
      // The Open timeout hasn't yet expired. Throw a CircuitBreakerOpen exception to
      // inform the caller that the call was not actually attempted,
      // and return the most recent exception received.
      throw new CircuitBreakerOpenException(stateStore.LastException);
    }
    ...

Para utilizar um objeto CircuitBreaker para proteger uma operação, uma aplicação cria uma instância da classe CircuitBreaker e invoca o método ExecuteAction, especificando a operação de forma a ser executada como o parâmetro.To use a CircuitBreaker object to protect an operation, an application creates an instance of the CircuitBreaker class and invokes the ExecuteAction method, specifying the operation to be performed as the parameter. A aplicação deverá ser preparada para apanhar a exceção CircuitBreakerOpenException se a operação falhar, pois o disjuntor automático está aberto.The application should be prepared to catch the CircuitBreakerOpenException exception if the operation fails because the circuit breaker is open. O código seguinte mostra um exemplo:The following code shows an example:

var breaker = new CircuitBreaker();

try
{
  breaker.ExecuteAction(() =>
  {
    // Operation protected by the circuit breaker.
    ...
  });
}
catch (CircuitBreakerOpenException ex)
{
  // Perform some different action when the breaker is open.
  // Last exception details are in the inner exception.
  ...
}
catch (Exception ex)
{
  ...
}

Os padrões seguintes podem também ser úteis ao implementar este padrão:The following patterns might also be useful when implementing this pattern:

  • Padrão repetição.Retry pattern. Descreve como uma aplicação pode processar falhas previstas e temporárias quando tentar ligar a um recurso ou serviço de rede, ao repetir de forma transparente uma operação que falhou anteriormente.Describes how an application can handle anticipated temporary failures when it tries to connect to a service or network resource by transparently retrying an operation that has previously failed.

  • Padrão de monitorização do ponto final do Estado de funcionamento.Health Endpoint Monitoring pattern. Um disjuntor automático pode conseguir testar o estado de um serviço ao enviar um pedido ao ponto final exposto pelo serviço.A circuit breaker might be able to test the health of a service by sending a request to an endpoint exposed by the service. O serviço deve devolver informações a indicar o seu estado.The service should return information indicating its status.