Padrão RepetiçãoRetry pattern

Permita que uma aplicação processe falhas transitórias quando tentar ligar a um recurso ou serviço de rede, ao repetir de forma transparente uma operação que falhou.Enable an application to handle transient failures when it tries to connect to a service or network resource, by transparently retrying a failed operation. Este procedimento pode melhorar a estabilidade da aplicação.This can improve the stability of the application.

Contexto e problemaContext and problem

Uma aplicação que comunica com elementos em execução na cloud tem de ser sensível às falhas transitórias que podem ocorrer neste ambiente.An application that communicates with elements running in the cloud has to be sensitive to the transient faults that can occur in this environment. As falhas incluem a perda momentânea de conectividade de rede para componentes e serviços, a indisponibilidade temporária de um serviço ou os tempos limite que ocorrem quando um serviço está ocupado.Faults include the momentary loss of network connectivity to components and services, the temporary unavailability of a service, or timeouts that occur when a service is busy.

Por norma, estas falhas corrigem-se automaticamente e, se a ação que acionou uma falha se repetir após um atraso adequado, é provável que seja concluída com sucesso.These faults are typically self-correcting, and if the action that triggered a fault is repeated after a suitable delay it's likely to be successful. Por exemplo, um serviço de base de dados que está a processar um grande número de pedidos simultâneos pode implementar uma estratégia de limitação que rejeita temporariamente pedidos adicionais até que a carga de trabalho tenha abrandado.For example, a database service that's processing a large number of concurrent requests can implement a throttling strategy that temporarily rejects any further requests until its workload has eased. Uma aplicação a tentar aceder à base de dados pode não conseguir estabelecer ligação, mas se tentar novamente, depois de um atraso, pode ser bem-sucedida.An application trying to access the database might fail to connect, but if it tries again after a delay it might succeed.

SoluçãoSolution

Na cloud, as falhas transitórias não são invulgares e uma aplicação deve ser concebida para as processar de forma sofisticada e transparente.In the cloud, transient faults aren't uncommon and an application should be designed to handle them elegantly and transparently. Tal minimiza os efeitos provocados pelas falhas nas tarefas de negócios que a aplicação está a realizar.This minimizes the effects faults can have on the business tasks the application is performing.

Se uma aplicação detetar uma falha ao tentar enviar um pedido para um serviço remoto, esta poderá lidar com a falha com recurso às seguintes estratégias:If an application detects a failure when it tries to send a request to a remote service, it can handle the failure using the following strategies:

  • Cancelar.Cancel. Se a falha indicar que não é transitória ou que é pouco provável que seja bem-sucedida se repetida, a aplicação deverá cancelar a operação e comunicar uma exceção.If the fault indicates that the failure isn't transient or is unlikely to be successful if repeated, the application should cancel the operation and report an exception. Por exemplo, uma falha de autenticação causada pela introdução de credenciais inválidas é pouco provável que seja bem-sucedida, independentemente do número de tentativas.For example, an authentication failure caused by providing invalid credentials is not likely to succeed no matter how many times it's attempted.

  • Repetir.Retry. Se a falha específica comunicada for invulgar ou rara, poderá ter sido causada por circunstâncias invulgares, tal como um pacote de rede que ficou danificado aquando da sua transmissão.If the specific fault reported is unusual or rare, it might have been caused by unusual circumstances such as a network packet becoming corrupted while it was being transmitted. Neste caso, a aplicação pode repetir de imediato o pedido em falha, uma vez que é pouco provável que a falha se repita e o pedido vai provavelmente ser bem-sucedido.In this case, the application could retry the failing request again immediately because the same failure is unlikely to be repeated and the request will probably be successful.

  • Tente novamente após o atraso.Retry after delay. Se a falha for provocada por uma ou mais falhas comuns de conectividade ou indisponibilidade, a rede ou o serviço poderá precisar de um curto período de tempo enquanto os problemas de conectividade são corrigidos ou o registo de tarefas pendentes do trabalho é resolvido.If the fault is caused by one of the more commonplace connectivity or busy failures, the network or service might need a short period while the connectivity issues are corrected or the backlog of work is cleared. A aplicação deve aguardar por um período de tempo adequado antes de repetir o pedido.The application should wait for a suitable time before retrying the request.

Para as falhas transitórias mais comuns, o período entre repetições deve ser selecionado para distribuir os pedidos por várias instâncias da aplicação o mais uniformemente possível.For the more common transient failures, the period between retries should be chosen to spread requests from multiple instances of the application as evenly as possible. Tal reduz a possibilidade de um serviço indisponível continuar a ser sobrecarregado.This reduces the chance of a busy service continuing to be overloaded. Se muitas instâncias de uma aplicação estiverem continuamente a sobrecarregar um serviço com pedidos de repetição, o serviço precisará de mais tempo para recuperar.If many instances of an application are continually overwhelming a service with retry requests, it'll take the service longer to recover.

Se o pedido continuar a falhar, a aplicação poderá aguardar e efetuar outra tentativa.If the request still fails, the application can wait and make another attempt. Se necessário, este processo pode ser repetido com cada vez mais atrasos entre as tentativas de repetição, até que o número máximo de pedidos seja atingido.If necessary, this process can be repeated with increasing delays between retry attempts, until some maximum number of requests have been attempted. O atraso pode ser aumentado incrementalmente ou exponencialmente, dependendo do tipo de falha e a probabilidade de este ser corrigido durante este período.The delay can be increased incrementally or exponentially, depending on the type of failure and the probability that it'll be corrected during this time.

O diagrama seguinte ilustra a invocação de uma operação num serviço alojado com este padrão.The following diagram illustrates invoking an operation in a hosted service using this pattern. Se o pedido não for bem-sucedido após um número predefinido de tentativas, a aplicação deverá tratar a falha como uma exceção e processá-la em conformidade.If the request is unsuccessful after a predefined number of attempts, the application should treat the fault as an exception and handle it accordingly.

Figura 1 – Invocar uma operação num serviço alojado com o padrão Repetição

A aplicação deve moldar todas as tentativas de acesso a um serviço remoto no código que implementa uma política de repetição correspondente a uma das estratégias listadas acima.The application should wrap all attempts to access a remote service in code that implements a retry policy matching one of the strategies listed above. Os pedidos enviados para diferentes serviços podem estar sujeitos a políticas diferentes.Requests sent to different services can be subject to different policies. Alguns fornecedores disponibilizam bibliotecas que implementam as políticas de repetição, onde a aplicação pode especificar o número máximo de tentativas, o tempo entre as tentativas de repetição e outros parâmetros.Some vendors provide libraries that implement retry policies, where the application can specify the maximum number of retries, the time between retry attempts, and other parameters.

Uma aplicação deve registar os detalhes das falhas e as operações com falhas.An application should log the details of faults and failing operations. Estas informações são úteis para os operadores.This information is useful to operators. Se um serviço estiver frequentemente indisponível ou ocupado, isso poderá muitas vezes significar que o serviço esgotou os recursos.If a service is frequently unavailable or busy, it's often because the service has exhausted its resources. Pode reduzir a frequência destas falhas ao aumentar horizontalmente o serviço.You can reduce the frequency of these faults by scaling out the service. Por exemplo, se um serviço de base de dados estiver continuamente sobrecarregado, poderá ser vantajoso dividir a base de dados em partições e distribuir a carga por vários servidores.For example, if a database service is continually overloaded, it might be beneficial to partition the database and spread the load across multiple servers.

O Microsoft Entity Framework fornece instalações para repetir as operações da base de dados.Microsoft Entity Framework provides facilities for retrying database operations. Além disso, a maioria dos serviços do Azure e os SDKs do cliente incluem um mecanismo de repetição.Also, most Azure services and client SDKs include a retry mechanism. Para obter mais informações, veja Orientações do mecanismo de repetição para serviços específicos.For more information, see Retry guidance for specific services.

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.

A política de repetição deve ser ajustada para corresponder aos requisitos comerciais da aplicação e à natureza da falha.The retry policy should be tuned to match the business requirements of the application and the nature of the failure. Para algumas operações não críticas, é melhor falhar e adaptar-se depressa ao invés de repetir várias vezes e afetar o débito da aplicação.For some noncritical operations, it's better to fail fast rather than retry several times and impact the throughput of the application. Por exemplo, numa aplicação Web interativa a aceder a um serviço remoto, é melhor falhar após um número menor de tentativas com apenas um pequeno atraso entre tentativas de repetição e apresentar uma mensagem adequada ao utilizador (por exemplo, “Tente novamente mais tarde”).For example, in an interactive web application accessing a remote service, it's better to fail after a smaller number of retries with only a short delay between retry attempts, and display a suitable message to the user (for example, “please try again later”). Para uma aplicação de lote, pode ser mais adequado aumentar o número de tentativas de repetição com um atraso a aumentar exponencialmente entre tentativas.For a batch application, it might be more appropriate to increase the number of retry attempts with an exponentially increasing delay between attempts.

Uma política de repetição agressiva com um atraso mínimo entre tentativas e um grande número de tentativas pode degradar ainda mais um serviço ocupado em execução perto ou no limite da capacidade.An aggressive retry policy with minimal delay between attempts, and a large number of retries, could further degrade a busy service that's running close to or at capacity. Esta política de repetição também poderá afetar a capacidade de resposta da aplicação se continuar a tentar realizar uma operação com falha.This retry policy could also affect the responsiveness of the application if it's continually trying to perform a failing operation.

Se um pedido continuar a falhar após um número significativo de tentativas, será melhor para a aplicação impedir futuros pedidos de acesso ao mesmo recurso e simplesmente comunicar uma falha.If a request still fails after a significant number of retries, it's better for the application to prevent further requests going to the same resource and simply report a failure immediately. Quando o período expira, a aplicação pode permitir provisoriamente o envio de um ou mais pedidos para ver se forem bem-sucedidos.When the period expires, the application can tentatively allow one or more requests through to see whether they're successful. Para obter mais detalhes sobre esta estratégia, veja o Padrão Disjuntor Automático.For more details of this strategy, see the Circuit Breaker pattern.

Considere se a operação é idempotente.Consider whether the operation is idempotent. Se for, é inerentemente seguro repetir.If so, it's inherently safe to retry. Caso contrário, as repetições podem fazer com que a operação seja executada mais do que uma vez, com efeitos secundários indesejados.Otherwise, retries could cause the operation to be executed more than once, with unintended side effects. Por exemplo, um serviço pode receber o pedido, processar o pedido com sucesso, mas falhar o envio de uma resposta.For example, a service might receive the request, process the request successfully, but fail to send a response. Nessa altura, a lógica de repetição pode reenviar o pedido, partindo do princípio de que o primeiro pedido não foi recebido.At that point, the retry logic might re-send the request, assuming that the first request wasn't received.

Um pedido para um serviço pode falhar por diversos motivos desencadeados por diferentes exceções, dependendo da natureza da falha.A request to a service can fail for a variety of reasons raising different exceptions depending on the nature of the failure. Algumas exceções indicam uma falha que pode ser resolvida rapidamente, enquanto outras indicam que a falha vai durar mais tempo.Some exceptions indicate a failure that can be resolved quickly, while others indicate that the failure is longer lasting. É útil para a política de repetição ajustar o tempo entre as tentativas de repetição com base no tipo de exceção.It's useful for the retry policy to adjust the time between retry attempts based on the type of the exception.

Considere a forma como a repetição de uma operação que faz parte de uma transação vai afetar a consistência global da transação.Consider how retrying an operation that's part of a transaction will affect the overall transaction consistency. Ajuste a política de repetição para operações transacionais para maximizar a hipótese de sucesso e reduzir a necessidade de anular todos os passos da transação.Fine tune the retry policy for transactional operations to maximize the chance of success and reduce the need to undo all the transaction steps.

Confirme se todo o código de repetição foi inteiramente testado face a uma variedade de condições de falha.Ensure that all retry code is fully tested against a variety of failure conditions. Verifique se não afeta gravemente o desempenho ou a fiabilidade da aplicação, provoca uma carga excessiva nos serviços e recursos ou gera condições race ou estrangulamentos.Check that it doesn't severely impact the performance or reliability of the application, cause excessive load on services and resources, or generate race conditions or bottlenecks.

Implemente a lógica de repetição apenas quando o contexto completo de uma operação com falha for compreendido.Implement retry logic only where the full context of a failing operation is understood. Por exemplo, se uma tarefa que contém uma política de repetição invocar outra tarefa que também contém uma política de repetição, esta camada adicional de tentativas poderá adicionar grandes atrasos ao processamento.For example, if a task that contains a retry policy invokes another task that also contains a retry policy, this extra layer of retries can add long delays to the processing. Pode ser melhor configurar a tarefa de nível inferior para falhar e adaptar-se depressa e comunicar o motivo da falha à tarefa que a invocou.It might be better to configure the lower-level task to fail fast and report the reason for the failure back to the task that invoked it. Esta tarefa de nível mais elevado pode, em seguida, processar a falha com base na sua própria política.This higher-level task can then handle the failure based on its own policy.

É importante registar todas as falhas de conectividade que dão origem a uma repetição para que seja possível identificar os problemas subjacentes com a aplicação, os serviços ou os recursos.It's important to log all connectivity failures that cause a retry so that underlying problems with the application, services, or resources can be identified.

Investigue as falhas com a maior probabilidade de ocorrência num serviço ou recurso para descobrir a probabilidade de serem de longa duração ou terminais.Investigate the faults that are most likely to occur for a service or a resource to discover if they're likely to be long lasting or terminal. Se forem, será melhor processar a falha como uma exceção.If they are, it's better to handle the fault as an exception. A aplicação pode comunicar ou registar a exceção e, em seguida, tentar continuar a invocar um serviço alternativo (se disponível) ou oferecer a funcionalidade degradada.The application can report or log the exception, and then try to continue either by invoking an alternative service (if one is available), or by offering degraded functionality. Para obter mais informações sobre como detetar e processar falhas de longa duração, veja o Padrão Disjunto Automático.For more information on how to detect and handle long-lasting faults, see the Circuit Breaker pattern.

Quando utilizar este padrãoWhen to use this pattern

Utilize este padrão se houver a possibilidade de uma aplicação experienciar falhas transitórias à medida que interage com um serviço remoto ou acede a um recurso remoto.Use this pattern when an application could experience transient faults as it interacts with a remote service or accesses a remote resource. Espera-se que estas falhas sejam de curta duração e que a repetição de um pedido que falhou anteriormente seja concluída com sucesso numa tentativa subsequente.These faults are expected to be short lived, and repeating a request that has previously failed could succeed on a subsequent attempt.

Este padrão pode não ser prático:This pattern might not be useful:

  • Quando há uma probabilidade de uma falha ser de longa duração, uma vez que tal pode afetar a capacidade de resposta de uma aplicação.When a fault is likely to be long lasting, because this can affect the responsiveness of an application. A aplicação pode estar a perder tempo e recursos ao tentar repetir um pedido que muito provavelmente vai falhar.The application might be wasting time and resources trying to repeat a request that's likely to fail.
  • Para o processamento de falhas que não se devem a falhas transitórias, tal como exceções internas causadas por erros na lógica de negócio de uma aplicação.For handling failures that aren't due to transient faults, such as internal exceptions caused by errors in the business logic of an application.
  • Como alternativa para resolver problemas de escalabilidade num sistema.As an alternative to addressing scalability issues in a system. Se uma aplicação experienciar repetidas falhas de indisponibilidade, isso é sinal frequente de que o serviço ou o recurso a serem acedidos deverão ser aumentados verticalmente.If an application experiences frequent busy faults, it's often a sign that the service or resource being accessed should be scaled up.

ExemploExample

Este exemplo em C# ilustra uma implementação do padrão Repetição.This example in C# illustrates an implementation of the Retry pattern. O método OperationWithBasicRetryAsync, mostrado abaixo, invoca um serviço externo assíncrono através do método TransientOperationAsync.The OperationWithBasicRetryAsync method, shown below, invokes an external service asynchronously through the TransientOperationAsync method. Os detalhes do método TransientOperationAsync serão específicos ao serviço e são omitidos do código de exemplo.The details of the TransientOperationAsync method will be specific to the service and are omitted from the sample code.

private int retryCount = 3;
private readonly TimeSpan delay = TimeSpan.FromSeconds(5);

public async Task OperationWithBasicRetryAsync()
{
  int currentRetry = 0;

  for (;;)
  {
    try
    {
      // Call external service.
      await TransientOperationAsync();

      // Return or break.
      break;
    }
    catch (Exception ex)
    {
      Trace.TraceError("Operation Exception");

      currentRetry++;

      // Check if the exception thrown was a transient exception
      // based on the logic in the error detection strategy.
      // Determine whether to retry the operation, as well as how
      // long to wait, based on the retry strategy.
      if (currentRetry > this.retryCount || !IsTransient(ex))
      {
        // If this isn't a transient error or we shouldn't retry,
        // rethrow the exception.
        throw;
      }
    }

    // Wait to retry the operation.
    // Consider calculating an exponential delay here and
    // using a strategy best suited for the operation and fault.
    await Task.Delay(delay);
  }
}

// Async method that wraps a call to a remote service (details not shown).
private async Task TransientOperationAsync()
{
  ...
}

A instrução que invoca este método está contida num bloco try/catch moldado num ciclo.The statement that invokes this method is contained in a try/catch block wrapped in a for loop. O ciclo “for” sairá se a chamada para o método TransientOperationAsync for bem-sucedida sem gerar uma exceção.The for loop exits if the call to the TransientOperationAsync method succeeds without throwing an exception. Se o método TransientOperationAsync falhar, o bloco catch examinará o motivo da falha.If the TransientOperationAsync method fails, the catch block examines the reason for the failure. Se for considerado um erro transitório, o código aguardará durante um pequeno período antes de repetir a operação.If it's believed to be a transient error the code waits for a short delay before retrying the operation.

O ciclo “for” também controla o número de vezes que a operação foi tentada e, se o código falhar três vezes, a exceção é assumida como sendo de maior duração.The for loop also tracks the number of times that the operation has been attempted, and if the code fails three times the exception is assumed to be more long lasting. Se a exceção não for transitória ou for de longa duração, o processador catch emitirá uma exceção.If the exception isn't transient or it's long lasting, the catch handler throws an exception. Esta exceção sai do ciclo “for” e deve ser detetada pelo código que invoca o método OperationWithBasicRetryAsync.This exception exits the for loop and should be caught by the code that invokes the OperationWithBasicRetryAsync method.

O método IsTransient mostrado abaixo procura um conjunto específico de exceções relevantes para o ambiente no qual o código é executado.The IsTransient method, shown below, checks for a specific set of exceptions that are relevant to the environment the code is run in. A definição de uma exceção transitória varia de acordo com os recursos a serem acedidos e o ambiente no qual a operação está a ser realizada.The definition of a transient exception will vary according to the resources being accessed and the environment the operation is being performed in.

private bool IsTransient(Exception ex)
{
  // Determine if the exception is transient.
  // In some cases this is as simple as checking the exception type, in other
  // cases it might be necessary to inspect other properties of the exception.
  if (ex is OperationTransientException)
    return true;

  var webException = ex as WebException;
  if (webException != null)
  {
    // If the web exception contains one of the following status values
    // it might be transient.
    return new[] {WebExceptionStatus.ConnectionClosed,
                  WebExceptionStatus.Timeout,
                  WebExceptionStatus.RequestCanceled }.
            Contains(webException.Status);
  }

  // Additional exception checking logic goes here.
  return false;
}
  • Padrão Disjuntor Automático.Circuit Breaker pattern. No caso de prever uma falha de longa duração, poderá ser mais apropriado implementar o padrão Disjuntor Automático.If a failure is expected to be more long lasting, it might be more appropriate to implement the Circuit Breaker pattern. A combinação dos padrões de repetição e de disjuntor fornece uma abordagem abrangente para lidar com falhas.Combining the Retry and Circuit Breaker patterns provides a comprehensive approach to handling faults.

  • Para a maioria dos serviços do Azure, os SDKs do cliente incluem lógica de repetição interna.For most Azure services, the client SDKs include built-in retry logic. Para obter mais informações, consulte diretrizes de repetição para serviços do Azure.For more information, see Retry guidance for Azure services.

  • Antes de gravar a lógica de repetição personalizada, considere usar uma estrutura geral, como Polly para .net ou Resilience4j para Java.Before writing custom retry logic, consider using a general framework such as Polly for .NET or Resilience4j for Java.