Appeler les services gRPC avec le client .NET
Remarque
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 9 de cet article.
Avertissement
Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la Stratégie de prise en charge de .NET et .NET Core. Pour la version actuelle, consultez la version .NET 8 de cet article.
Important
Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.
Pour la version actuelle, consultez la version .NET 9 de cet article.
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 serviceGreeter.SayHello
de façon asynchrone. Peut être attendu.GreeterClient.SayHello
- appelle le serviceGreeter.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
}
Le type retourné par le lancement d’un appel de diffusion en continu serveur implémente IDisposable
. Supprimez toujours les appels de diffusion en continu pour vous assurer qu’ils sont arrêtés et que toutes les ressources sont nettoyées.
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
Le type retourné par le lancement d’un appel de diffusion en continu client implémente IDisposable
. Supprimez toujours les appels de diffusion en continu pour vous assurer qu’ils sont arrêtés et que toutes les ressources sont nettoyées.
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 :
- Démarre un nouvel appel de diffusion en continu bidirectionnel en appelant
EchoClient.Echo
. - Crée une tâche en arrière-plan pour lire les messages du service à l’aide de
ResponseStream.ReadAllAsync()
. - Envoie des messages au serveur avec
RequestStream.WriteAsync
. - Avertit le serveur qu’il a terminé d’envoyer des messages avec
RequestStream.CompleteAsync()
. - 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.
Le type retourné par le lancement d’un appel de diffusion en continu bidirectionnel implémente IDisposable
. Supprimez toujours les appels de diffusion en continu pour vous assurer qu’ils sont arrêtés et que toutes les ressources sont nettoyées.
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.