Anropa gRPC-tjänster med .NET-klienten
Not
Det här är inte den senaste versionen av den här artikeln. Den aktuella versionen finns i .NET 9-versionen av den här artikeln.
Varning
Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i .NET och .NET Core Support Policy. Den aktuella versionen finns i .NET 9-versionen av den här artikeln.
Viktig
Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.
Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.
Ett .NET gRPC-klientbibliotek finns i Grpc.Net.Client NuGet-paketet. I det här dokumentet förklaras hur du:
- Konfigurera en gRPC-klient för att anropa gRPC-tjänster.
- Gör gRPC-anrop till unary, serverströmning, klientströmning och dubbelriktade strömningsmetoder.
Konfigurera gRPC-klient
gRPC-klienter är konkreta klienttyper som genereras från .proto
filer. Den konkreta gRPC-klienten har metoder som översätter till gRPC-tjänsten i filen .proto
. En tjänst med namnet Greeter
genererar till exempel en GreeterClient
typ med metoder för att anropa tjänsten.
En gRPC-klient skapas från en kanal. Börja med att använda GrpcChannel.ForAddress
för att skapa en kanal och använd sedan kanalen för att skapa en gRPC-klient:
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greet.GreeterClient(channel);
En kanal representerar en långvarig anslutning till en gRPC-tjänst. När en kanal skapas konfigureras den med alternativ som rör att anropa en tjänst. Till exempel kan den HttpClient
som används för att göra anrop, maximal storlek på skicka och ta emot meddelanden och loggning anges på GrpcChannelOptions
och användas med GrpcChannel.ForAddress
. En fullständig lista över alternativ finns i klientkonfigurationsalternativ.
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
Konfigurera TLS
En gRPC-klient måste använda samma säkerhet på anslutningsnivå som den anropade tjänsten. gRPC-klienten Transport Layer Security (TLS) konfigureras när gRPC-kanalen skapas. En gRPC-klient genererar ett fel när den anropar en tjänst och säkerheten på anslutningsnivå för kanalen och tjänsten inte matchar.
Om du vill konfigurera en gRPC-kanal så att den använder TLS kontrollerar du att serveradressen börjar med https
. Till exempel använder GrpcChannel.ForAddress("https://localhost:5001")
HTTPS-protokoll. GRPC-kanalen förhandlar automatiskt om en anslutning som skyddas av TLS och använder en säker anslutning för att göra gRPC-anrop.
Tips
gRPC stöder klientcertifikatautentisering via TLS. Information om hur du konfigurerar klientcertifikat med en gRPC-kanal finns i autentisering och auktorisering i gRPC för ASP.NET Core.
Om du vill anropa oskyddade gRPC-tjänster kontrollerar du att serveradressen börjar med http
. Till exempel använder GrpcChannel.ForAddress("http://localhost:5000")
HTTP-protokoll. I .NET Core 3.1 krävs ytterligare konfiguration för att anropa osäkra gRPC-tjänster med .NET-klienten.
Klientprestanda
Kanal- och klientprestanda och -användning:
- Att skapa en kanal kan vara en dyr åtgärd. Att återanvända en kanal för gRPC-anrop ger prestandafördelar.
- En kanal hanterar anslutningar till servern. Om anslutningen stängs eller går förlorad återansluter kanalen automatiskt nästa gång ett gRPC-anrop görs.
- gRPC-klienter skapas med kanaler. gRPC-klienter är lätta objekt och behöver inte cachelagras eller återanvändas.
- Flera gRPC-klienter kan skapas från en kanal, inklusive olika typer av klienter.
- En kanal och klienter som skapats från kanalen kan på ett säkert sätt användas av flera trådar.
- Klienter som skapats från kanalen kan göra flera samtidiga anrop.
GrpcChannel.ForAddress
är inte det enda alternativet för att skapa en gRPC-klient. Om du anropar gRPC-tjänster från en ASP.NET Core-app bör du överväga gRPC-klientfabriksintegrering. gRPC-integrering med HttpClientFactory
erbjuder ett centraliserat alternativ till att skapa gRPC-klienter.
Utföra gRPC-anrop
Ett gRPC-anrop initieras genom att anropa en metod på klienten. gRPC-klienten hanterar meddelande serialisering och adresserar gRPC-anropet till rätt tjänst.
gRPC har olika typer av metoder. Hur klienten används för att göra ett gRPC-anrop beror på vilken typ av metod som anropas. GRPC-metodtyperna är:
- Unär
- Serverströmning
- Klientströmning
- Dubbelriktad direktuppspelning
Unaryanrop
Ett unary-anrop börjar med att klienten skickar ett meddelande om begäran. Ett svarsmeddelande returneras när tjänsten är klar.
var client = new Greet.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });
Console.WriteLine("Greeting: " + response.Message);
// Greeting: Hello World
Varje unary-tjänstmetod i filen .proto
resulterar i två .NET-metoder på den konkreta gRPC-klienttypen för att anropa metoden: en asynkron metod och en blockeringsmetod. På GreeterClient
finns det till exempel två sätt att anropa SayHello
:
-
GreeterClient.SayHelloAsync
– anroparGreeter.SayHello
tjänsten asynkront. Kan avvaktas. -
GreeterClient.SayHello
– anroparGreeter.SayHello
-tjänsten och blockerar tills den är klar. Använd inte i asynkron kod.
Serveruppspelningsanrop
Ett serverströmningsanrop börjar med att klienten skickar ett begärandemeddelande.
ResponseStream.MoveNext()
läser meddelanden som strömmas från tjänsten. Serverströmningsanropet är slutfört när ResponseStream.MoveNext()
returnerar 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
}
När du använder C# 8 eller senare kan await foreach
syntax användas för att läsa meddelanden. Metoden IAsyncStreamReader<T>.ReadAllAsync()
-tilläggsmetod läser alla meddelanden från svarsströmmen:
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
}
Den typ som returneras från att starta ett serverströmmande anrop implementerar IDisposable
. Ta alltid bort ett strömningsanrop för att säkerställa att det stoppas och att alla resurser rensas.
Klientuppspelningsanrop
Ett klientströmningsanrop startar utan att klienten skickar ett meddelande. Klienten kan välja att skicka meddelanden med RequestStream.WriteAsync
. När klienten har skickat meddelanden ska RequestStream.CompleteAsync()
anropas för att meddela tjänsten. Anropet är klart när tjänsten returnerar ett svarsmeddelande.
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
Den typ som returneras när ett klientströmningsanrop startas implementerar IDisposable
. Ta alltid bort ett strömningsanrop för att säkerställa att det stoppas och att alla resurser rensas.
Dubbelriktad strömmande anrop
Ett dubbelriktat strömmande anrop startar utan att klienten skickar ett meddelande. Klienten kan välja att skicka meddelanden med RequestStream.WriteAsync
. Meddelanden som strömmas från tjänsten är tillgängliga med ResponseStream.MoveNext()
eller ResponseStream.ReadAllAsync()
. Det dubbelriktade strömningsanropet är slutfört när ResponseStream
inte har fler meddelanden.
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;
För bästa prestanda och för att undvika onödiga fel i klienten och tjänsten kan du försöka slutföra dubbelriktade strömningsanrop på ett korrekt sätt. Ett dubbelriktade anrop slutförs korrekt när servern har läst igenom begärandeströmmen och klienten har läst färdigt svarsströmmen. Föregående exempelanrop är ett exempel på ett dubbelriktat anrop som avslutas korrekt. Under samtalet med klienten:
- Startar ett nytt dubbelriktade strömningsanrop genom att anropa
EchoClient.Echo
. - Skapar en bakgrundsprocess för att läsa meddelanden från tjänsten med hjälp av
ResponseStream.ReadAllAsync()
. - Skickar meddelanden till servern med
RequestStream.WriteAsync
. - Notifierar servern att den har avslutat att skicka meddelanden med
RequestStream.CompleteAsync()
. - Väntar tills bakgrundsaktiviteten har läst alla inkommande meddelanden.
Under ett dubbelriktade strömningsanrop kan klienten och tjänsten när som helst skicka meddelanden till varandra. Den bästa klientlogik för att interagera med ett dubbelriktat anrop varierar beroende på tjänstlogik.
Typen som returneras när man startar ett dubbelriktat strömningsanrop implementerar IDisposable
. Ta alltid bort ett strömningsanrop för att säkerställa att det stoppas och att alla resurser rensas.
Åtkomst till gRPC-huvuden
gRPC-anrop returnerar svarshuvuden. HTTP-svarshuvuden skickar namn/värdemetadata om ett anrop som inte är relaterat till det returnerade meddelandet.
Rubriker är tillgängliga med hjälp av ResponseHeadersAsync
, som returnerar en samling metadata. Rubriker returneras vanligtvis med svarsmeddelandet. Därför måste ni invänta dem.
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;
ResponseHeadersAsync
användning:
- Måste invänta resultatet av
ResponseHeadersAsync
för att få rubrikssamlingen. - Behöver inte nås innan
ResponseAsync
(eller svarsströmmen vid direktuppspelning). Om ett svar har returnerats returnerarResponseHeadersAsync
rubriker direkt. - Utlöser ett undantag om det uppstod ett anslutnings- eller serverfel och rubrikerna inte returnerades för gRPC-anropet.
Åtkomst till gRPC-släpvagnar
gRPC-anrop kan returnera svarstrailers. Trailers används för att ange namn/värdemetadata om ett anrop. Trailers har liknande funktioner som HTTP-huvuden, men tas emot i slutet av anropet.
Trailers är tillgängliga med hjälp av GetTrailers()
, som returnerar en samling metadata. Släpvagnar returneras när svaret har slutförts. Därför måste du invänta alla svarsmeddelanden innan du träder in i trailers.
Unary- och klientströmningsanrop måste invänta ResponseAsync
innan du anropar 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");
Server- och dubbelriktade strömningsanrop måste slutföras genom att vänta på svarsströmmen innan du kan anropa 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");
Släpvagnar kan också nås från RpcException
. En tjänst kan returnera släpvagnar tillsammans med en gRPC-status som inte är OK. I det här fallet hämtas släpen från undantaget som genereras av gRPC-klienten:
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");
}
Konfigurera tidsgräns
Vi rekommenderar att du konfigurerar en tidsgräns för gRPC-samtal eftersom det ger en övre gräns för hur länge ett anrop kan köras. Det hindrar felaktiga tjänster från att köras för evigt och tömma serverresurserna. Tidsgränser är ett användbart verktyg för att skapa tillförlitliga appar.
Konfigurera CallOptions.Deadline
för att ange en tidsgräns för ett gRPC-anrop:
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.");
}
Mer information finns i Reliable gRPC-tjänster med tidsgränser och annullering.
Ytterligare resurser
ASP.NET Core