Share via


Services gRPC fiables avec échéances et annulation

Par James Newton-King

Les délais et l’annulation sont des fonctionnalités utilisées par les clients gRPC pour abandonner les appels en cours. Cet article explique pourquoi les délais et l’annulation sont importants et comment les utiliser dans les applications gRPC .NET.

Échéances

Une échéance permet à un client gRPC de spécifier la durée d’attente d’un appel. Lorsqu’une échéance est dépassée, l’appel est annulé. La définition d’une échéance est importante, car elle fournit une limite supérieure à la durée d’exécution d’un appel. Il empêche les services au comportement problématique de s’exécuter indéfiniment et d’épuiser les ressources du serveur. Les délais sont un outil utile pour créer des applications fiables et doivent être configurés.

Configuration de l’échéance :

  • Une échéance est configurée à l’aide de CallOptions.Deadline au moment où un appel est effectué.
  • Il n’existe aucune valeur d’échéance par défaut. Les appels gRPC ne sont pas limités dans le temps, sauf si une échéance est spécifiée.
  • Une échéance est l’heure UTC du dépassement de l’échéance. Par exemple, DateTime.UtcNow.AddSeconds(5) est une échéance de 5 secondes à partir de maintenant.
  • Si une heure passée ou actuelle est utilisée, l’appel dépasse immédiatement l’échéance.
  • L’échéance est envoyée avec l’appel gRPC au service et est suivie indépendamment par le client et le service. Il est possible qu’un appel gRPC se termine sur une machine, mais qu’au moment où la réponse est retournée au client, l’échéance a été dépassée.

Si une échéance est dépassée, le client et le service ont un comportement différent :

  • Le client abandonne immédiatement la requête HTTP sous-jacente et génère une erreur DeadlineExceeded. L’application cliente peut choisir d’intercepter l’erreur et d’afficher un message d’expiration de délai à l’utilisateur.
  • Sur le serveur, la requête HTTP en cours d’exécution est abandonnée et ServerCallContext.CancellationToken est levé. Bien que la requête HTTP soit abandonnée, l’appel gRPC continue de s’exécuter sur le serveur jusqu’à la fin de la méthode. Il est important que le jeton d’annulation soit passé à des méthodes asynchrones afin qu’elles soient annulées avec l’appel. Par exemple, en passant un jeton d’annulation aux requêtes de base de données asynchrones et aux requêtes HTTP. Le passage d’un jeton d’annulation permet à l’appel annulé de se terminer rapidement sur le serveur et de libérer les ressources pour d’autres appels.

Configurez CallOptions.Deadline pour définir une échéance pour un appel gRPC :

var client = new Greet.GreeterClient(channel);

try
{
    var response = await client.SayHelloAsync(
        new HelloRequest { Name = "World" },
        deadline: DateTime.UtcNow.AddSeconds(5));
    
    // Greeting: Hello World
    Console.WriteLine("Greeting: " + response.Message);
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.DeadlineExceeded)
{
    Console.WriteLine("Greeting timeout.");
}

Utilisation de ServerCallContext.CancellationToken dans un service gRPC :

public override async Task<HelloReply> SayHello(HelloRequest request,
    ServerCallContext context)
{
    var user = await _databaseContext.GetUserAsync(request.Name,
        context.CancellationToken);

    return new HelloReply { Message = "Hello " + user.DisplayName };
}

Échéances et nouvelles tentatives

Lorsqu’un appel gRPC est configuré avec la gestion des erreurs de nouvelle tentative et une échéance, l’échéance suit le temps de toutes les nouvelles tentatives pour un appel gRPC. Si l’échéance est dépassée, un appel gRPC abandonne immédiatement la requête HTTP sous-jacente, ignore les nouvelles tentatives restantes et génère une erreur DeadlineExceeded.

Propagation des échéances

Lorsqu’un appel gRPC est effectué à partir d’un service gRPC en cours d’exécution, l’échéance doit être propagée. Par exemple :

  1. L’application cliente appelle FrontendService.GetUser avec une échéance.
  2. FrontendService appelle UserService.GetUser. L’échéance spécifiée par le client doit être spécifiée avec le nouvel appel gRPC.
  3. UserService.GetUser reçoit l’échéance. Il expire correctement si l’échéance de l’application cliente est dépassée.

Le contexte d’appel indique l’échéance avec ServerCallContext.Deadline :

public override async Task<UserResponse> GetUser(UserRequest request,
    ServerCallContext context)
{
    var client = new User.UserServiceClient(_channel);
    var response = await client.GetUserAsync(
        new UserRequest { Id = request.Id },
        deadline: context.Deadline);

    return response;
}

La propagation manuelle des échéances peut être fastidieuse. L’échéance doit être passée à chaque appel, et il est facile de la manquer accidentellement. Une solution automatique est disponible avec la fabrique de clients gRPC. Spécification de EnableCallContextPropagation :

  • Propage automatiquement le jeton d’échéance et d’annulation aux appels enfants.
  • Ne propage pas l’échéance si l’appel enfant spécifie une échéance plus courte. Par exemple, une échéance propagée de 10 secondes n’est pas utilisée si un appel enfant spécifie une nouvelle échéance de 5 secondes avec CallOptions.Deadline. Lorsque plusieurs échéances sont disponibles, la plus courte est utilisée.
  • C’est un excellent moyen de s’assurer que les scénarios gRPC complexes et imbriqués propagent toujours l’échéance et l’annulation.
services
    .AddGrpcClient<User.UserServiceClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation();

Pour plus d’informations, consultez Intégration de la fabrique de clients gRPC dans .NET.

Annulation

L’annulation permet à un client gRPC d’annuler les appels de longue durée qui ne sont plus nécessaires. Par exemple, un appel gRPC qui diffuse des mises à jour en temps réel est démarré lorsque l’utilisateur visite une page d’un site web. Le flux doit être annulé lorsque l’utilisateur quitte la page.

Un appel gRPC peut être annulé dans le client en passant un jeton d’annulation avec CallOptions.CancelToken ou en appelant Dispose sur l’appel.

private AsyncServerStreamingCall<HelloReply> _call;

public void StartStream()
{
    _call = client.SayHellos(new HelloRequest { Name = "World" });

    // Read response in background task.
    _ = Task.Run(async () =>
    {
        await foreach (var response in _call.ResponseStream.ReadAllAsync())
        {
            Console.WriteLine("Greeting: " + response.Message);
        }
    });
}

public void StopStream()
{
    _call.Dispose();
}

Les services gRPC qui peuvent être annulés doivent :

  • Passer ServerCallContext.CancellationToken aux méthodes asynchrones. L’annulation des méthodes asynchrones permet à l’appel sur le serveur de se terminer rapidement.
  • Propager le jeton d’annulation aux appels enfants. La propagation du jeton d’annulation garantit que les appels enfants sont annulés avec leur parent. La fabrique de clients gRPC et EnableCallContextPropagation() propagent automatiquement le jeton d’annulation.

Ressources supplémentaires