Modèle Nouvelle tentativeRetry pattern

Permettez à une application de gérer les défaillances temporaires quand elle tente de se connecter à un service ou à une ressource réseau en réessayant d’exécuter en toute transparence une opération qui a échoué.Enable an application to handle transient failures when it tries to connect to a service or network resource, by transparently retrying a failed operation. Cela peut améliorer la stabilité de l’application.This can improve the stability of the application.

Contexte et problèmeContext and problem

Une application qui communique avec des éléments en cours d’exécution dans le cloud doit être sensible aux défaillances temporaires qui peuvent se produire dans cet environnement.An application that communicates with elements running in the cloud has to be sensitive to the transient faults that can occur in this environment. Les défaillances incluent la perte momentanée de la connectivité réseau aux composants et aux services, l’indisponibilité passagère d’un service ou les expirations de délai qui surviennent lorsqu’un service est occupé.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.

Ces défaillances se corrigent généralement d’elles-mêmes, et si l’action qui a déclenché une défaillance est répétée après un délai approprié, il est probable qu’elle aboutisse.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. Par exemple, un service de base de données qui traite un grand nombre de requêtes simultanées peut implémenter une stratégie de limitation qui rejette temporairement toute requête supplémentaire jusqu’à allègement de sa charge de travail.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. Il est possible qu’une application qui tente d’accéder à la base de données ne parvienne pas à se connecter, mais si elle tente à nouveau après un certain délai, elle peut réussir.An application trying to access the database might fail to connect, but if it tries again after a delay it might succeed.

SolutionSolution

Dans le cloud, les défaillances temporaires ne sont pas rares et une application doit être conçue pour les gérer avec soin et en toute transparence.In the cloud, transient faults aren't uncommon and an application should be designed to handle them elegantly and transparently. Cela permet de limiter les conséquences que peuvent avoir les défaillances sur les tâches professionnelles exécutées par l’application.This minimizes the effects faults can have on the business tasks the application is performing.

Si une application détecte un échec lorsqu’elle tente d’envoyer une requête à un service distant, elle peut gérer l’échec à l’aide des méthodes suivantes :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:

  • Annulation.Cancel. Si l’erreur indique que l’échec n’est pas temporaire ou qu’il est probable qu’il se répète, l’application doit annuler l’opération et générer une exception.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. Par exemple, il est fort probable qu’un échec d’authentification dû à la saisie d’informations d’identification non valides se répète, peu importe le nombre de tentatives.For example, an authentication failure caused by providing invalid credentials is not likely to succeed no matter how many times it's attempted.

  • Nouvelle tentative.Retry. Si l’erreur spécifique signalée est inhabituelle ou rare, elle peut avoir été provoquée par des circonstances inhabituelles telles qu’un paquet réseau endommagé au cours de sa transmission.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. Dans ce cas, l’application pourrait relancer immédiatement la requête ayant échoué, car la même erreur est peu susceptible de se reproduire et qu’il est probable que la requête aboutisse.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.

  • Nouvelle tentative après un délai.Retry after delay. Si l’échec est causé par un problème courant de connectivité ou de disponibilité, le réseau ou le service nécessitera certainement un certain délai le temps que les problèmes de connectivité soient corrigés ou que la file d’attente de tâches soit vidée.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. L’application doit attendre un délai approprié avant de relancer la requête.The application should wait for a suitable time before retrying the request.

Pour les échecs temporaires les plus courants, le délai entre chaque tentative doit être choisi de façon à pouvoir répartir les requêtes provenant de plusieurs instances de l’application de manière aussi équitable que possible.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. Cela réduit le risque de surcharger encore davantage un service déjà occupé.This reduces the chance of a busy service continuing to be overloaded. Si de nombreuses instances d’une application inondent en permanence un service de demandes de nouvelles tentatives, le service aura besoin de plus de temps pour récupérer un fonctionnement normal.If many instances of an application are continually overwhelming a service with retry requests, it'll take the service longer to recover.

Si la demande n’aboutit toujours pas, l’application peut attendre et effectuer une nouvelle tentative.If the request still fails, the application can wait and make another attempt. Le cas échéant, ce processus peut être répété en augmentant le délai d’attente entre chaque nouvelle tentative, jusqu’à ce qu’un nombre maximal de demandes a été atteint.If necessary, this process can be repeated with increasing delays between retry attempts, until some maximum number of requests have been attempted. Le délai d’attente peut être augmenté de façon incrémentielle ou exponentielle, selon le type d’échec et la probabilité de résolution de l’erreur dans le délai spécifié.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.

Le diagramme suivant illustre l’appel d’une opération dans un service hébergé à l’aide de ce modèle.The following diagram illustrates invoking an operation in a hosted service using this pattern. Si la requête échoue après un nombre prédéfini de tentatives, l’application doit traiter l’erreur en tant qu’exception et la gérer en conséquence.If the request is unsuccessful after a predefined number of attempts, the application should treat the fault as an exception and handle it accordingly.

Figure 1 : Appel d’une opération dans un service hébergé à l’aide du modèle Nouvelle tentative

L’application doit inclure toutes les tentatives d’accès à un service distant dans un code qui implémente une stratégie de nouvelle tentative correspondant à l’une des méthodes répertoriées ci-dessus.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. Les requêtes envoyées aux différents services peuvent être soumises à des stratégies différentes.Requests sent to different services can be subject to different policies. Certains éditeurs proposent des bibliothèques qui implémentent des stratégies de nouvelle tentative, où l’application peut spécifier le nombre maximal de nouvelles tentatives, le délai d’attente entre chaque tentative, ainsi que d’autres paramètres.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.

Une application doit consigner les détails des erreurs et des opérations ayant échoué.An application should log the details of faults and failing operations. Ces informations sont utiles pour les opérateurs.This information is useful to operators. Si un service est fréquemment occupé ou indisponible, cela signifie souvent que le service a épuisé ses ressources.If a service is frequently unavailable or busy, it's often because the service has exhausted its resources. Vous pouvez réduire la fréquence de ces erreurs en procédant à une montée en charge du service.You can reduce the frequency of these faults by scaling out the service. Par exemple, si un service de base de données est surchargé en permanence, il peut être utile de partitionner la base de données et de répartir la charge sur plusieurs serveurs.For example, if a database service is continually overloaded, it might be beneficial to partition the database and spread the load across multiple servers.

Microsoft Entity Framework fournit des fonctionnalités pour relancer les opérations de base de données.Microsoft Entity Framework provides facilities for retrying database operations. De même, la plupart des services Azure et des kits de développement logiciel (SDK) clients incluent un mécanisme de nouvelle tentative.Also, most Azure services and client SDKs include a retry mechanism. Pour plus d’informations, consultez Guide du mécanisme de nouvelle tentative relatif aux différents services.For more information, see Retry guidance for specific services.

Problèmes et considérationsIssues and considerations

Prenez en compte les points suivants quand vous choisissez comment implémenter ce modèle.You should consider the following points when deciding how to implement this pattern.

La stratégie de nouvelle tentative doit être configurée en fonction des exigences métiers de l’application et de la nature de l’échec.The retry policy should be tuned to match the business requirements of the application and the nature of the failure. Pour certaines opérations non critiques, il est préférable d’effectuer un Fail-fast plutôt que d’effectuer plusieurs autres tentatives qui peuvent avoir un impact sur le débit de l’application.For some noncritical operations, it's better to fail fast rather than retry several times and impact the throughput of the application. Par exemple, dans le cas d’une application web interactive souhaitant accéder à un service distant, il est préférable d’accepter un échec après un plus petit nombre de tentatives avec un délai d’attente court entre chaque nouvelle tentative et d’afficher un message à l’intention de l’utilisateur (par exemple, « Veuillez réessayer ultérieurement »).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”). Pour une application de traitement par lot, il peut être plus judicieux d’augmenter le nombre de tentatives de connexion en augmentant de manière exponentielle le délai d’attente entre chaque tentative.For a batch application, it might be more appropriate to increase the number of retry attempts with an exponentially increasing delay between attempts.

Une stratégie de relance agressive avec un délai minimal entre les tentatives et un grand nombre de nouvelles tentatives peut affecter encore davantage les performances d’un service occupé qui a déjà atteint ou est sur le point d’atteindre sa capacité maximale.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. Cette stratégie de relance peut également affecter la réactivité de l’application si elle tente en permanence de relancer une opération ayant échoué.This retry policy could also affect the responsiveness of the application if it's continually trying to perform a failing operation.

Si une requête continue d’échouer après un nombre important de nouvelles tentatives, il est préférable que l’application cesse d’envoyer de nouvelles requêtes à la même ressource et qu’elle signale immédiatement l’erreur.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. Lorsque le délai expire, l’application peut, à titre d’essai, envoyer une ou plusieurs requêtes pour voir si elles aboutissent.When the period expires, the application can tentatively allow one or more requests through to see whether they're successful. Pour plus d’informations sur cette stratégie, consultez l’article Modèle Disjoncteur.For more details of this strategy, see the Circuit Breaker pattern.

Déterminez si l’opération est idempotente.Consider whether the operation is idempotent. Si c’est le cas, une nouvelle tentative peut être lancée sans risque.If so, it's inherently safe to retry. Dans le cas contraire, les nouvelles tentatives pourraient entraîner plusieurs exécutions de la même opération, causant des effets secondaires inattendus.Otherwise, retries could cause the operation to be executed more than once, with unintended side effects. Par exemple, un service peut recevoir la requête, la traiter avec succès, mais ne pas parvenir à envoyer une réponse.For example, a service might receive the request, process the request successfully, but fail to send a response. À ce stade, la logique de nouvelle tentative peut consister à renvoyer la requête en supposant que la première requête n’a pas été reçue.At that point, the retry logic might re-send the request, assuming that the first request wasn't received.

Une requête envoyée à un service peut échouer pour diverses raisons et déclencher différentes exceptions selon la nature de l’échec.A request to a service can fail for a variety of reasons raising different exceptions depending on the nature of the failure. Certaines exceptions indiquent un échec qui peut être résolu rapidement, tandis que d’autres indiquent une défaillance plus complexe.Some exceptions indicate a failure that can be resolved quickly, while others indicate that the failure is longer lasting. Il est recommandé que la stratégie de relance configure le délai d’attente entre les nouvelles tentatives en fonction du type d’exception générée.It's useful for the retry policy to adjust the time between retry attempts based on the type of the exception.

Tenez compte de la manière dont la relance d’une opération faisant partie d’une transaction affectera la cohérence globale de la transaction.Consider how retrying an operation that's part of a transaction will affect the overall transaction consistency. Ajustez la stratégie de relance pour les opérations transactionnelles afin de maximiser les chances de réussite et d’éviter autant que possible d’avoir à annuler toutes les étapes de la transaction.Fine tune the retry policy for transactional operations to maximize the chance of success and reduce the need to undo all the transaction steps.

Vérifiez que l’ensemble du code de relance a été testé pour un large éventail de conditions d’échec.Ensure that all retry code is fully tested against a variety of failure conditions. Vérifiez qu’il ne nuit pas gravement aux performances ou à la fiabilité de l’application, qu’il ne créé pas une charge excessive au niveau des services et des ressources, et qu’il ne génère pas des conditions de concurrence ou des goulots d’étranglement.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.

Implémentez la logique de nouvelle tentative uniquement lorsque vous avez identifié le contexte à l’origine de l’échec d’une opération.Implement retry logic only where the full context of a failing operation is understood. Par exemple, si une tâche qui contient une stratégie de nouvelle tentative appelle une autre tâche qui contient également une stratégie de nouvelle tentative, cette couche supplémentaire de nouvelles tentatives peut retarder de manière considérable le processus de traitement.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. Il peut être préférable d’effectuer un Fail-fast de la tâche de niveau inférieur et d’indiquer la raison de l’échec à la tâche qui l’a appelée.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. Cette tâche de niveau supérieur peut alors gérer l’échec en fonction de sa propre stratégie.This higher-level task can then handle the failure based on its own policy.

Il est important de consigner tous les échecs de connexion qui entraînent une nouvelle tentative afin que les problèmes sous-jacents liés à l’application, aux services ou aux ressources puissent être identifiés.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.

Recherchez les erreurs qui sont les plus susceptibles de se produire pour un service ou une ressource afin de découvrir si elles sont susceptibles d’être longues ou définitives.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. Si elles le sont, il est préférable de gérer l’erreur comme une exception.If they are, it's better to handle the fault as an exception. L’application peut signaler ou consigner l’exception, puis retenter l’opération en appelant un autre service (le cas échéant) ou en fournissant un niveau de fonctionnalité inférieur.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. Pour plus d’informations sur la façon de détecter et de gérer les erreurs de longue durée, consultez l’article Modèle Disjoncteur.For more information on how to detect and handle long-lasting faults, see the Circuit Breaker pattern.

Quand utiliser ce modèleWhen to use this pattern

Utilisez ce modèle lorsqu’une application rencontre des erreurs temporaires au moment d’interagir avec un service distant ou d’accéder à une ressource distante.Use this pattern when an application could experience transient faults as it interacts with a remote service or accesses a remote resource. Ces erreurs sont normalement de courte durée et une requête ayant échoué précédemment peut aboutir lors d’une nouvelle tentative.These faults are expected to be short lived, and repeating a request that has previously failed could succeed on a subsequent attempt.

Ce modèle peut ne pas avoir d’utilité dans les cas suivants :This pattern might not be useful:

  • Lorsqu’une erreur est susceptible d’être de longue durée, car cela peut affecter la réactivité d’une application.When a fault is likely to be long lasting, because this can affect the responsiveness of an application. L’application peut perdre du temps et des ressources à essayer de répéter une requête qui est susceptible d’échouer.The application might be wasting time and resources trying to repeat a request that's likely to fail.
  • Pour gérer les échecs qui ne sont pas dus à des erreurs temporaires, tels que les exceptions internes générées par des erreurs dans la logique métier d’une application.For handling failures that aren't due to transient faults, such as internal exceptions caused by errors in the business logic of an application.
  • Pour éviter de résoudre les problèmes d’extensibilité dans un système.As an alternative to addressing scalability issues in a system. Si une application génère fréquemment des erreurs liées à la disponibilité, cela indique souvent que le service ou la ressource concerné(e) doit être mis(e) à l’échelle.If an application experiences frequent busy faults, it's often a sign that the service or resource being accessed should be scaled up.

ExemplesExample

Cet exemple en C# illustre une implémentation du modèle Nouvelle tentative.This example in C# illustrates an implementation of the Retry pattern. La méthode OperationWithBasicRetryAsync, illustrée ci-dessous, appelle un service externe en mode asynchrone via la méthode TransientOperationAsync.The OperationWithBasicRetryAsync method, shown below, invokes an external service asynchronously through the TransientOperationAsync method. Les détails de la méthode TransientOperationAsync sont propres au service et ne sont donc pas spécifiés dans l’exemple de code.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()
{
  ...
}

L’instruction qui appelle cette méthode se trouve dans un bloc try/catch encapsulé dans une boucle for.The statement that invokes this method is contained in a try/catch block wrapped in a for loop. La boucle for s’arrête si l’appel envoyé à la méthode TransientOperationAsync aboutit sans générer d’exception.The for loop exits if the call to the TransientOperationAsync method succeeds without throwing an exception. Si la méthode TransientOperationAsync échoue, le bloc catch recherche la raison de l’échec.If the TransientOperationAsync method fails, the catch block examines the reason for the failure. Si l’erreur est considérée comme temporaire, le code attend un court délai avant de retenter l’opération.If it's believed to be a transient error the code waits for a short delay before retrying the operation.

La boucle for enregistre également le nombre de fois où l’opération a été tentée. Si le code échoue à trois reprises, l’exception est considérée comme plus longue.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. Si l’exception n’est pas temporaire ou qu’elle est de longue durée, le gestionnaire catch génère une exception.If the exception isn't transient or it's long lasting, the catch handler throws an exception. Cette exception ferme la boucle for et doit être interceptée par le code qui appelle la méthode OperationWithBasicRetryAsync.This exception exits the for loop and should be caught by the code that invokes the OperationWithBasicRetryAsync method.

La méthode IsTransient, illustrée ci-dessous, recherche un ensemble spécifique d’exceptions en fonction de l’environnement dans lequel le code est exécuté.The IsTransient method, shown below, checks for a specific set of exceptions that are relevant to the environment the code is run in. La définition d’une exception temporaire varie selon les ressources ciblées et l’environnement dans lequel l’opération est exécutée.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;
}