Appeler les services gRPC avec le client .NET

Une bibliothèque cliente gRPC .NET est disponible dans le package NuGet Grpc.Net.Client. Ce document explique comment :

  • Configurer un client gRPC pour appeler les services gRPC.
  • Effectuer des appels gRPC aux méthodes unaires, de diffusion en continu de serveur, de diffusion en continu client et de diffusion en continu bidirectionnelle.

Configurer le client gRPC

Les clients gRPC sont des types de client concrets générés à partir de fichiers .proto. Le client gRPC concret a des méthodes qui sont traduites pour le service gRPC dans le fichier .proto. Par exemple, un service appelé Greeter génère un type GreeterClient avec des méthodes pour appeler le service.

Un client gRPC est créé à partir d’un canal. Commencez par utiliser GrpcChannel.ForAddress pour créer un canal, puis utilisez le canal pour créer un client gRPC :

var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greet.GreeterClient(channel);

Un canal représente une connexion de longue durée à un service gRPC. Lorsqu’un canal est créé, il est configuré avec des options liées à l’appel d’un service. Par exemple, le HttpClient utilisé pour effectuer des appels, la taille maximale des messages d’envoi et de réception et la journalisation peuvent être spécifiés sur GrpcChannelOptions et utilisés avec GrpcChannel.ForAddress. Pour obtenir la liste complète des options, consultez Options de configuration du client.

var channel = GrpcChannel.ForAddress("https://localhost:5001");

var greeterClient = new Greet.GreeterClient(channel);
var counterClient = new Count.CounterClient(channel);

// Use clients to call gRPC services

Configurer TLS

Un client gRPC doit utiliser la même sécurité au niveau de la connexion que le service appelé. Le protocole TLS (Transport Layer Security) du client gRPC est configuré lors de la création du canal gRPC. Un client gRPC génère une erreur lorsqu’il appelle un service et que la sécurité au niveau de la connexion du canal et du service ne correspond pas.

Pour configurer un canal gRPC pour utiliser TLS, vérifiez que l’adresse du serveur commence par https. Par exemple, GrpcChannel.ForAddress("https://localhost:5001") utilise le protocole HTTPS. Le canal gRPC négocie automatiquement une connexion sécurisée par TLS et utilise une connexion sécurisée pour effectuer des appels gRPC.

Conseil

gRPC prend en charge l’authentification par certificat client sur TLS. Pour plus d’informations sur la configuration des certificats clients avec un canal gRPC, consultez Authentification et autorisation dans gRPC pour ASP.NET Core.

Pour appeler des services gRPC non sécurisés, vérifiez que l’adresse du serveur commence par http. Par exemple, GrpcChannel.ForAddress("http://localhost:5000") utilise le protocole HTTP. Dans .NET Core 3.1, une configuration supplémentaire est nécessaire pour appeler des services gRPC non sécurisés avec le client .NET.

Performances du client

Performances et utilisation du canal et du client :

  • La création d’un canal peut être une opération coûteuse. La réutilisation d’un canal pour les appels gRPC offre des avantages en matière de performances.
  • Un canal gère les connexions au serveur. Si la connexion est fermée ou perdue, le canal reconnecte automatiquement la prochaine fois qu’un appel gRPC est effectué.
  • Les clients gRPC sont créés avec des canaux. Les clients gRPC sont des objets légers qui n’ont pas besoin d’être mis en cache ou réutilisés.
  • Plusieurs clients gRPC peuvent être créés à partir d’un canal, y compris différents types de clients.
  • Un canal et les clients créés à partir du canal peuvent être utilisés en toute sécurité par plusieurs threads.
  • Les clients créés à partir du canal peuvent effectuer plusieurs appels simultanés.

GrpcChannel.ForAddress n’est pas la seule option pour créer un client gRPC. Si vous appelez des services gRPC à partir d’une application ASP.NET Core, envisagez d’intégrer la fabrique de clients gRPC. L’intégration de gRPC à HttpClientFactory offre une alternative centralisée à la création de clients gRPC.

Notes

L’appel de gRPC sur HTTP/2 avec Grpc.Net.Client n’est actuellement pas pris en charge sur Xamarin. Nous nous efforçons d’améliorer la prise en charge de HTTP/2 pour une prochaine version de Xamarin. Grpc.Core et gRPC-Web sont des alternatives viables qui fonctionnent aujourd’hui.

Effectuer des appels gRPC

Un appel gRPC est lancé en appelant une méthode sur le client. Le client gRPC gère la sérialisation des messages et l’adressage de l’appel gRPC au service approprié.

gRPC a différents types de méthodes. La façon dont le client est utilisé pour effectuer un appel gRPC dépend du type de la méthode appelée. Les types de méthodes gRPC sont les suivants :

  • Unaire
  • Diffusion en continu du serveur
  • Diffusion en continu du client
  • Diffusion en continu bidirectionnelle

Appel unaire

Un appel unaire commence par l’envoi d’un message de requête par le client. Un message de réponse est retourné à la fin du service.

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

Console.WriteLine("Greeting: " + response.Message);
// Greeting: Hello World

Chaque méthode de service unaire dans le fichier .proto génère deux méthodes .NET sur le type de client gRPC concret pour appeler la méthode : une méthode asynchrone et une méthode bloquante. Par exemple, sur GreeterClient, il existe deux façons d’appeler SayHello :

  • GreeterClient.SayHelloAsync - appelle le service Greeter.SayHello de façon asynchrone. Peut être attendu.
  • GreeterClient.SayHello - appelle le service Greeter.SayHello et se bloque jusqu’à ce qu’il soit terminé. Ne l’utilisez pas dans du code asynchrone.

Appel de diffusion en continu de serveur

Un appel de diffusion en continu de serveur commence par l’envoi d’un message de requête par le client. ResponseStream.MoveNext() lit les messages diffusés à partir du service. L’appel de diffusion en continu du serveur est terminé quand ResponseStream.MoveNext() retourne false.

var client = new Greet.GreeterClient(channel);
using var call = client.SayHellos(new HelloRequest { Name = "World" });

while (await call.ResponseStream.MoveNext())
{
    Console.WriteLine("Greeting: " + call.ResponseStream.Current.Message);
    // "Greeting: Hello World" is written multiple times
}

Lors de l’utilisation de C# 8 ou d’une version ultérieure, la syntaxe await foreach peut être utilisée pour lire les messages. La IAsyncStreamReader<T>.ReadAllAsync() méthode d’extension lit tous les messages du flux de réponse :

var client = new Greet.GreeterClient(channel);
using var call = client.SayHellos(new HelloRequest { Name = "World" });

await foreach (var response in call.ResponseStream.ReadAllAsync())
{
    Console.WriteLine("Greeting: " + response.Message);
    // "Greeting: Hello World" is written multiple times
}

Appel de diffusion en continu client

Un appel de diffusion en continu client démarre sans que le client envoie de message. Le client peut choisir d’envoyer des messages avec RequestStream.WriteAsync. Lorsque le client a terminé d’envoyer des messages, RequestStream.CompleteAsync() doit être appelé pour avertir le service. L’appel est terminé lorsque le service retourne un message de réponse.

var client = new Counter.CounterClient(channel);
using var call = client.AccumulateCount();

for (var i = 0; i < 3; i++)
{
    await call.RequestStream.WriteAsync(new CounterRequest { Count = 1 });
}
await call.RequestStream.CompleteAsync();

var response = await call;
Console.WriteLine($"Count: {response.Count}");
// Count: 3

Appel de diffusion en continu bidirectionnel

Un appel de diffusion en continu bidirectionnel démarre sans que le client envoie de message. Le client peut choisir d’envoyer des messages avec RequestStream.WriteAsync. Les messages diffusés en continu à partir du service sont accessibles avec ResponseStream.MoveNext() ou ResponseStream.ReadAllAsync(). L’appel de diffusion en continu bidirectionnel est terminé lorsque ResponseStream n’a plus de messages.

var client = new Echo.EchoClient(channel);
using var call = client.Echo();

Console.WriteLine("Starting background task to receive messages");
var readTask = Task.Run(async () =>
{
    await foreach (var response in call.ResponseStream.ReadAllAsync())
    {
        Console.WriteLine(response.Message);
        // Echo messages sent to the service
    }
});

Console.WriteLine("Starting to send messages");
Console.WriteLine("Type a message to echo then press enter.");
while (true)
{
    var result = Console.ReadLine();
    if (string.IsNullOrEmpty(result))
    {
        break;
    }

    await call.RequestStream.WriteAsync(new EchoMessage { Message = result });
}

Console.WriteLine("Disconnecting");
await call.RequestStream.CompleteAsync();
await readTask;

Pour de meilleures performances et pour éviter les erreurs inutiles dans le client et le service, essayez d’effectuer correctement les appels de diffusion en continu bidirectionnels. Un appel bidirectionnel se termine correctement lorsque le serveur a terminé de lire le flux de requête et que le client a terminé de lire le flux de réponse. L’exemple d’appel précédent est un exemple d’appel bidirectionnel qui se termine normalement. Dans l’appel, le client :

  1. Démarre un nouvel appel de diffusion en continu bidirectionnel en appelant EchoClient.Echo.
  2. Crée une tâche en arrière-plan pour lire les messages du service à l’aide de ResponseStream.ReadAllAsync().
  3. Envoie des messages au serveur avec RequestStream.WriteAsync.
  4. Avertit le serveur qu’il a terminé d’envoyer des messages avec RequestStream.CompleteAsync().
  5. Attend que la tâche en arrière-plan ait lu tous les messages entrants.

Pendant un appel de diffusion en continu bidirectionnel, le client et le service peuvent s’envoyer des messages l’un à l’autre à tout moment. La meilleure logique client pour interagir avec un appel bidirectionnel varie en fonction de la logique de service.

Accéder aux en-têtes gRPC

Les appels gRPC retournent des en-têtes de réponse. Les en-têtes de réponse HTTP passent les métadonnées nom/valeur d’un appel qui n’est pas lié au message retourné.

Les en-têtes sont accessibles à l’aide de ResponseHeadersAsync, qui retourne une collection de métadonnées. Les en-têtes sont généralement retournés avec le message de réponse ; par conséquent, vous devez les attendre.

var client = new Greet.GreeterClient(channel);
using var call = client.SayHelloAsync(new HelloRequest { Name = "World" });

var headers = await call.ResponseHeadersAsync;
var myValue = headers.GetValue("my-trailer-name");

var response = await call.ResponseAsync;

Utilisation de ResponseHeadersAsync :

  • Doit attendre le résultat de ResponseHeadersAsync pour obtenir la collection d’en-têtes.
  • Il n’est pas nécessaire d’y accéder avant ResponseAsync (ou le flux de réponse lors de la diffusion en continu). Si une réponse a été retournée, ResponseHeadersAsync retourne les en-têtes instantanément.
  • Lève une exception s’il y a une erreur de connexion ou de serveur et que les en-têtes n’ont pas été retournés pour l’appel gRPC.

Accéder aux amorces gRPC

Les appels gRPC peuvent renvoyer des amorces de réponse. Les amorces sont utilisées pour fournir des métadonnées nom/valeur sur un appel. Les amorces fournissent des fonctionnalités similaires aux en-têtes HTTP, mais sont reçues à la fin de l’appel.

Les amorces sont accessibles à l’aide de GetTrailers(), qui retourne une collection de métadonnées. Les amorces sont retournées une fois la réponse terminée. Par conséquent, vous devez attendre tous les messages de réponse avant d’accéder aux amorces.

Les appels de diffusion en continu unaire et client doivent attendre ResponseAsync avant d’appeler GetTrailers() :

var client = new Greet.GreeterClient(channel);
using var call = client.SayHelloAsync(new HelloRequest { Name = "World" });
var response = await call.ResponseAsync;

Console.WriteLine("Greeting: " + response.Message);
// Greeting: Hello World

var trailers = call.GetTrailers();
var myValue = trailers.GetValue("my-trailer-name");

Les appels de diffusion en continu bidirectionnel et de serveur doivent se terminer en attendant le flux de réponse avant d’appeler GetTrailers() :

var client = new Greet.GreeterClient(channel);
using var call = client.SayHellos(new HelloRequest { Name = "World" });

await foreach (var response in call.ResponseStream.ReadAllAsync())
{
    Console.WriteLine("Greeting: " + response.Message);
    // "Greeting: Hello World" is written multiple times
}

var trailers = call.GetTrailers();
var myValue = trailers.GetValue("my-trailer-name");

Les amorces sont également accessibles à partir de RpcException. Un service peut retourner des amorces avec un état gRPC non OK. Dans ce cas, les amorces sont récupérées à partir de l’exception levée par le client gRPC :

var client = new Greet.GreeterClient(channel);
string myValue = null;

try
{
    using var call = client.SayHelloAsync(new HelloRequest { Name = "World" });
    var response = await call.ResponseAsync;

    Console.WriteLine("Greeting: " + response.Message);
    // Greeting: Hello World

    var trailers = call.GetTrailers();
    myValue = trailers.GetValue("my-trailer-name");
}
catch (RpcException ex)
{
    var trailers = ex.Trailers;
    myValue = trailers.GetValue("my-trailer-name");
}

Configurer l’échéance

La configuration d’une échéance d’appel gRPC est recommandée, 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 échéances sont un outil utile pour créer des applications fiables.

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.");
}

Pour plus d’informations, consultez Services gRPC fiables avec des échéances et des annulations.

Ressources supplémentaires