Dela via


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 – anropar Greeter.SayHello tjänsten asynkront. Kan avvaktas.
  • GreeterClient.SayHello – anropar Greeter.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:

  1. Startar ett nytt dubbelriktade strömningsanrop genom att anropa EchoClient.Echo.
  2. Skapar en bakgrundsprocess för att läsa meddelanden från tjänsten med hjälp av ResponseStream.ReadAllAsync().
  3. Skickar meddelanden till servern med RequestStream.WriteAsync.
  4. Notifierar servern att den har avslutat att skicka meddelanden med RequestStream.CompleteAsync().
  5. 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 returnerar ResponseHeadersAsync 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