Partager via


Gestion des erreurs temporaires avec nouvelles tentatives gRPC

Par James Newton-King

Les nouvelles tentatives gRPC sont une fonctionnalité qui permet aux clients gRPC de réessayer automatiquement les appels ayant échoué. Cet article explique comment configurer une stratégie de nouvelles tentatives pour rendre les applications gRPC résilientes et tolérantes aux pannes dans .NET.

Les nouvelles tentatives gRPC nécessitent Grpc.Net.Client version 2.36.0 ou ultérieure.

Gestion des erreurs temporaires

Les appels gRPC peuvent être interrompus par des erreurs temporaires. Les erreurs temporaires sont les suivantes :

  • Perte momentanée de connexion réseau.
  • Indisponibilité temporaire d’un service.
  • Dépassement d’un délai d’expiration dû à la charge du serveur.

Lorsqu’un appel gRPC est interrompu, le client lève une exception RpcException avec des détails sur l’erreur. L’application cliente doit intercepter l’exception et choisir comment gérer l’erreur.

var client = new Greeter.GreeterClient(channel);
try
{
    var response = await client.SayHelloAsync(
        new HelloRequest { Name = ".NET" });

    Console.WriteLine("From server: " + response.Message);
}
catch (RpcException ex)
{
    // Write logic to inspect the error and retry
    // if the error is from a transient fault.
}

La duplication d’une logique de nouvelle tentative dans une application est détaillée et sujette aux erreurs. Heureusement, le client gRPC .NET dispose d’une prise en charge intégrée des nouvelles tentatives automatiques.

Configurer une stratégie de nouvelles tentatives gRPC

Une stratégie de nouvelles tentatives est configurée une fois, lors de la création d’un canal gRPC :

var defaultMethodConfig = new MethodConfig
{
    Names = { MethodName.Default },
    RetryPolicy = new RetryPolicy
    {
        MaxAttempts = 5,
        InitialBackoff = TimeSpan.FromSeconds(1),
        MaxBackoff = TimeSpan.FromSeconds(5),
        BackoffMultiplier = 1.5,
        RetryableStatusCodes = { StatusCode.Unavailable }
    }
};

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
    ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
});

Le code précédent :

  • Crée un MethodConfig. Les stratégies de nouvelles tentatives peuvent être configurées par méthode et les méthodes sont mises en correspondance à l’aide de la propriété Names. Cette méthode est configurée avec MethodName.Default, elle est donc appliquée à toutes les méthodes gRPC appelées par ce canal.
  • Configure une stratégie de nouvelles tentatives. Cette stratégie indique aux clients de réessayer automatiquement les appels gRPC qui échouent avec le code d’état Unavailable.
  • Configure le canal créé pour utiliser la stratégie de nouvelles tentatives en définissant GrpcChannelOptions.ServiceConfig.

Les clients gRPC créés avec le canal réessayent automatiquement les appels ayant échoué :

var client = new Greeter.GreeterClient(channel);
var response = await client.SayHelloAsync(
    new HelloRequest { Name = ".NET" });

Console.WriteLine("From server: " + response.Message);

Quand les nouvelles tentatives sont valides

Les appels sont retentés dans les cas suivants :

  • Le code d’état défaillant correspond à une valeur dans RetryableStatusCodes.
  • Le nombre précédent de tentatives est inférieur à MaxAttempts.
  • L’appel n’a pas été validé.
  • L’échéance n’a pas été dépassée.

Un appel gRPC est validé dans deux scénarios :

  • Le client reçoit les en-têtes de réponse. Les en-têtes de réponse sont envoyés par le serveur quand ServerCallContext.WriteResponseHeadersAsync est appelé ou quand le premier message est écrit dans le flux de réponse du serveur.
  • Le message sortant du client (ou les messages en cas de diffusion en continu) a dépassé la taille maximale de la mémoire tampon du client. MaxRetryBufferSize et MaxRetryBufferPerCallSize sont configurés sur le canal.

Les appels validés ne réessayent pas, quel que soit le code d’état ou le nombre précédent de tentatives.

Appels de diffusion en continu

Les appels de diffusion en continu peuvent être utilisés avec les nouvelles tentatives gRPC, mais il existe des considérations importantes lorsqu’ils sont utilisés ensemble :

  • Diffusion en continu du serveur, diffusion en continu bidirectionnelle : les appels RPC de diffusion en continu qui retournent plusieurs messages du serveur ne réessayent pas une fois le premier message reçu. Les applications doivent ajouter une logique supplémentaire pour rétablir manuellement les appels de diffusion en continu de serveur et bidirectionnelle.
  • Diffusion en continu du client, diffusion en continu bidirectionnelle : les appels RPC de diffusion en continu qui envoient plusieurs messages au serveur ne réessayent pas si les messages sortants ont dépassé la taille maximale de la mémoire tampon du client. La taille maximale de la mémoire tampon peut être augmentée avec la configuration.

Pour plus d’informations, consultez Quand les nouvelles tentatives sont valides.

Délai de nouvelle tentative d’interruption

Le délai d’interruption entre les nouvelles tentatives est configuré avec InitialBackoff, MaxBackoff et BackoffMultiplier. Pour plus d’informations sur chaque option, consultez la section Options de nouvelles tentatives gRPC.

Le délai réel entre les nouvelles tentatives est aléatoire. Un délai aléatoire entre 0 et l’interruption actuelle détermine quand la nouvelle tentative suivante sera effectuée. Tenez compte du fait que même avec une interruption exponentielle configurée, l’augmentation de l’interruption actuelle entre les tentatives n’entraîne pas toujours une augmentation du délai réel entre les tentatives. Le délai est aléatoire pour empêcher les nouvelles tentatives à partir de plusieurs appels de se mettre en cluster et de surcharger potentiellement le serveur.

Détecter les nouvelles tentatives avec des métadonnées

Les nouvelles tentatives gRPC peuvent être détectées par la présence de métadonnées grpc-previous-rpc-attempts. Métadonnées grpc-previous-rpc-attempts :

  • Elles sont automatiquement ajoutées aux appels retentés et envoyées au serveur.
  • Leur valeur représente le nombre de nouvelles tentatives précédentes.
  • Leur valeur est toujours un entier.

Prenons le scénario de nouvelles tentatives suivant :

  1. Le client effectue un appel gRPC au serveur.
  2. Le serveur échoue et retourne une réponse de code d’état de nouvelle tentative.
  3. Le client retente l’appel gRPC. Étant donné qu’il y a eu une tentative précédente, les métadonnées grpc-previous-rpc-attempts ont la valeur 1. Les métadonnées sont envoyées au serveur avec la nouvelle tentative.
  4. Le serveur réussit et retourne OK.
  5. Le client signale la réussite. grpc-previous-rpc-attempts est dans les métadonnées de réponse et a la valeur 1.

Les métadonnées grpc-previous-rpc-attempts ne sont pas présentes lors de l’appel gRPC initial, ont la valeur 1 pour la première nouvelle tentative, la valeur 2 pour la deuxième nouvelle tentative, etc.

Options de nouvelle tentative gRPC

Le tableau suivant décrit les options de configuration des stratégies de nouvelles tentatives gRPC :

Option Description
MaxAttempts Nombre maximal de tentatives d’appel, y compris la tentative d’origine. Cette valeur est limitée par GrpcChannelOptions.MaxRetryAttempts qui est par défaut de 5. Une valeur est requise et doit être supérieure à 1.
InitialBackoff Délai d’interruption initial entre les nouvelles tentatives. Un délai aléatoire entre 0 et l’interruption actuelle détermine quand la nouvelle tentative suivante sera effectuée. Après chaque tentative, l’interruption actuelle est multipliée par BackoffMultiplier. Une valeur est requise et doit être supérieure à zéro.
MaxBackoff L’interruption maximale place une limite supérieure à la croissance d’interruption exponentielle. Une valeur est requise et doit être supérieure à zéro.
BackoffMultiplier L’interruption est multipliée par cette valeur après chaque nouvelle tentative et augmente de façon exponentielle lorsque le multiplicateur est supérieur à 1. Une valeur est requise et doit être supérieure à zéro.
RetryableStatusCodes Collection de codes d’état. Un appel gRPC qui échoue avec un état correspondant est automatiquement retenté. Pour plus d’informations sur les codes d’état, consultez Codes d’état et leur utilisation dans gRPC. Au moins un code d’état de nouvelle tentative est requis.

Couverture

La couverture est une stratégie de nouvelles tentatives alternative. La couverture permet d’envoyer de manière agressive plusieurs copies d’un même appel gRPC sans attendre de réponse. Les appels gRPC couverts peuvent être exécutés plusieurs fois sur le serveur et le premier résultat réussi est utilisé. Il est important que la couverture soit activée uniquement pour les méthodes qui peuvent être exécutées plusieurs fois sans effet négatif.

La couverture a des avantages et des inconvénients par rapport aux nouvelles tentatives :

  • L’avantage de la couverture est qu’elle peut retourner un résultat réussi plus rapidement. Elle autorise plusieurs appels gRPC simultanément et se termine quand le premier résultat réussi est disponible.
  • L’inconvénient de la couverture est qu’elle est propice au gaspillage. Plusieurs appels sont effectués et peuvent tous réussir. Seul le premier résultat est utilisé et les autres sont ignorés.

Configurer une stratégie de couverture gRPC

Une stratégie de couverture est configurée comme une stratégie de nouvelles tentatives. Notez qu’une stratégie de couverture ne peut pas être combinée à une stratégie de nouvelles tentatives.

var defaultMethodConfig = new MethodConfig
{
    Names = { MethodName.Default },
    HedgingPolicy = new HedgingPolicy
    {
        MaxAttempts = 5,
        NonFatalStatusCodes = { StatusCode.Unavailable }
    }
};

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
    ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
});

Options de couverture gRPC

Le tableau suivant décrit les options de configuration des stratégies de couverture gRPC :

Option Description
MaxAttempts La stratégie de couverture enverra jusqu’à ce nombre d’appels. MaxAttempts représente le nombre total de tentatives, y compris la tentative d’origine. Cette valeur est limitée par GrpcChannelOptions.MaxRetryAttempts qui est par défaut de 5. Une valeur est requise qui doit être supérieure ou égale à 2.
HedgingDelay Le premier appel est envoyé immédiatement, les appels de couverture suivants sont retardés par cette valeur. Quand le retard est défini sur zéro ou null, tous les appels couverts sont envoyés immédiatement. HedgingDelay est facultatif et a la valeur par défaut zéro. Une valeur doit être égale ou supérieure à zéro.
NonFatalStatusCodes Une collection de codes d’état qui indiquent que d’autres appels de couverture peuvent encore réussir. Si un code d’état non irrécupérable est retourné par le serveur, les appels couverts continuent. Dans le cas contraire, les demandes en suspens sont annulées et l’erreur renvoyée à l’application. Pour plus d’informations sur les codes d’état, consultez Codes d’état et leur utilisation dans gRPC.

Ressources supplémentaires