Delen via


GRPC-services aanroepen met de .NET-client

Notitie

Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.

Waarschuwing

Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.

Belangrijk

Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.

Zie de .NET 9-versie van dit artikelvoor de huidige release.

Er is een .NET gRPC-clientbibliotheek beschikbaar in het Grpc.Net.Client NuGet-pakket. In dit document wordt uitgelegd hoe u:

  • Configureer een gRPC-client om gRPC-services aan te roepen.
  • Maak gRPC-aanroepen naar unaire, serverstreaming, clientstreaming en bidirectionele streamingmethoden.

gRPC-client configureren

gRPC-clients zijn concrete clienttypen die worden gegenereerd op basis van .proto bestanden. De concrete gRPC-client heeft methoden die vertalen naar de gRPC-service in het .proto-bestand. Een service met de naam Greeter genereert bijvoorbeeld een GreeterClient type met methoden om de service aan te roepen.

Er wordt een gRPC-client gemaakt op basis van een kanaal. Begin met het gebruik van GrpcChannel.ForAddress om een kanaal te maken en gebruik vervolgens het kanaal om een gRPC-client te maken:

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

Een kanaal vertegenwoordigt een langdurige verbinding met een gRPC-service. Wanneer een kanaal wordt gemaakt, wordt het geconfigureerd met opties die betrekking hebben op het aanroepen van een service. Bijvoorbeeld, de HttpClient die wordt gebruikt voor het maken van oproepen, de maximale verzend- en berichtgrootte, en de logboekregistratie kunnen worden gespecificeerd op GrpcChannelOptions en gebruikt met GrpcChannel.ForAddress. Zie clientconfiguratieoptiesvoor een volledige lijst met opties.

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

TLS configureren

Een gRPC-client moet dezelfde beveiliging op verbindingsniveau gebruiken als de aangeroepen service. gRPC client Transport Layer Security (TLS) wordt geconfigureerd wanneer het gRPC-kanaal wordt gemaakt. Een gRPC-client genereert een fout bij het aanroepen van een service en de beveiliging op verbindingsniveau van het kanaal en de service komen niet overeen.

Als u een gRPC-kanaal wilt configureren voor het gebruik van TLS, moet u ervoor zorgen dat het serveradres begint met https. GrpcChannel.ForAddress("https://localhost:5001") gebruikt bijvoorbeeld het HTTPS-protocol. Het gRPC-kanaal onderhandelt automatisch over een verbinding die wordt beveiligd door TLS en maakt gebruik van een beveiligde verbinding om gRPC-aanroepen te maken.

Fooi

gRPC ondersteunt verificatie van clientcertificaten via TLS. Zie Verificatie en autorisatie in gRPC voor ASP.NET Corevoor meer informatie over het configureren van clientcertificaten met een gRPC-kanaal.

Als u onbeveiligde gRPC-services wilt aanroepen, moet u ervoor zorgen dat het serveradres begint met http. GrpcChannel.ForAddress("http://localhost:5000") maakt bijvoorbeeld gebruik van het HTTP-protocol. In .NET Core 3.1 is aanvullende configuratie vereist voor het aanroepen van onveilige gRPC-services met de .NET-client.

Klantprestaties

Kanaal- en clientprestaties en -gebruik:

  • Het maken van een kanaal kan een dure bewerking zijn. Het opnieuw gebruiken van een kanaal voor gRPC-aanroepen biedt prestatievoordelen.
  • Een kanaal beheert verbindingen met de server. Als de verbinding is gesloten of verbroken, wordt het kanaal automatisch opnieuw verbonden wanneer de volgende keer een gRPC-aanroep wordt uitgevoerd.
  • gRPC-clients worden gemaakt met kanalen. gRPC-clients zijn lichtgewicht objecten en hoeven niet in de cache te worden opgeslagen of opnieuw te worden gebruikt.
  • Er kunnen meerdere gRPC-clients vanuit een kanaal worden gemaakt, waarbij het om verschillende typen clients kan gaan.
  • Een kanaal en clients die zijn gemaakt op basis van het kanaal, kunnen veilig worden gebruikt door meerdere threads.
  • Clients die zijn gemaakt op basis van het kanaal, kunnen meerdere gelijktijdige aanroepen uitvoeren.

GrpcChannel.ForAddress is niet de enige optie voor het maken van een gRPC-client. Als u gRPC-services oproept vanuit een ASP.NET Core-app, kunt u overwegen gRPC-client-factory-integratie. gRPC-integratie met HttpClientFactory biedt een gecentraliseerd alternatief voor het maken van gRPC-clients.

GRPC-aanroepen uitvoeren

Een gRPC-aanroep wordt gestart door een methode aan te roepen op de client. De gRPC-client verwerkt berichtserialisatie en adresseert de gRPC-aanroep naar de juiste service.

gRPC heeft verschillende soorten methoden. Hoe de client wordt gebruikt om een gRPC-aanroep te maken, is afhankelijk van het type methode dat wordt aangeroepen. De gRPC-methodetypen zijn:

  • Unaire
  • Serverstreaming
  • Client streaming
  • Bidirectioneel streamen

Unary-aanroep

Een unaire aanroep begint met de client die een aanvraagbericht verzendt. Er wordt een antwoordbericht geretourneerd wanneer de service is voltooid.

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

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

Elke unaire servicemethode in het bestand .proto resulteert in twee .NET-methoden op het concrete gRPC-clienttype voor het aanroepen van de methode: een asynchrone methode en een blokkeringsmethode. Op GreeterClient zijn er bijvoorbeeld twee manieren om SayHelloaan te roepen:

  • GreeterClient.SayHelloAsync - roept asynchroon de Greeter.SayHello-service aan. Kan afgewacht worden.
  • GreeterClient.SayHello roept de Greeter.SayHello-dienst aan en blokkeert totdat deze is voltooid. Gebruik dit niet in asynchrone code.

Oproep voor serverstreaming

Een streaming-aanroep van een server begint met de client die een aanvraagbericht verzendt. ResponseStream.MoveNext() berichten leest die vanuit de service zijn gestreamd. De streaming-aanroep van de server is voltooid wanneer ResponseStream.MoveNext()falseretourneert.

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
}

Wanneer u C# 8 of hoger gebruikt, kan de syntaxis van de await foreach worden gebruikt om berichten te lezen. Met de IAsyncStreamReader<T>.ReadAllAsync()-extensiemethode worden alle berichten uit de antwoordstroom gelezen:

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
}

Het type dat wordt geretourneerd van het starten van een serverstreaming-aanroep implementeert IDisposable. Verwijder altijd een streaming-aanroep om ervoor te zorgen dat deze wordt gestopt en alle resources worden opgeschoond.

Streaming-aanroep van de client

Een clientstream-oproep start zonder dat de client een bericht verstuurt. De client kan ervoor kiezen om berichten te verzenden met RequestStream.WriteAsync. Wanneer de client klaar is met het verzenden van berichten, moet RequestStream.CompleteAsync() worden aangeroepen om de service op de hoogte te stellen. De aanroep is voltooid wanneer de service een antwoordbericht retourneert.

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

Het type dat wordt geretourneerd bij het starten van een clientstreaming-aanroep implementeert IDisposable. Verwijder altijd een streaming-aanroep om ervoor te zorgen dat deze wordt gestopt en alle resources worden opgeschoond.

Bidirectionele streaming-oproep

Een bidirectionele streaming-oproep wordt gestart zonder dat de client een bericht verzendt. De client kan ervoor kiezen om berichten te verzenden met RequestStream.WriteAsync. Berichten die vanuit de service worden gestreamd, zijn toegankelijk met ResponseStream.MoveNext() of ResponseStream.ReadAllAsync(). De bidirectionele streaming-oproep is voltooid wanneer de ResponseStream geen berichten meer heeft.

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;

Voor de beste prestaties en om onnodige fouten in de client en service te voorkomen, probeert u bidirectionele streaming-aanroepen probleemloos te voltooien. Een bidirectionele aanroep wordt probleemloos voltooid wanneer de server klaar is met het lezen van de aanvraagstroom en de client klaar is met het lezen van de antwoordstroom. De voorgaande voorbeeldoproep is een voorbeeld van een bidirectionele aanroep die probleemloos eindigt. Tijdens het gesprek, de klant:

  1. Start een nieuwe bidirectionele streaming-oproep door EchoClient.Echoaan te roepen.
  2. Hiermee maakt u een achtergrondtaak om berichten van de service te lezen met behulp van ResponseStream.ReadAllAsync().
  3. Verzendt berichten naar de server met RequestStream.WriteAsync.
  4. Brengt de server op de hoogte dat het het verzenden van berichten met RequestStream.CompleteAsync()heeft voltooid.
  5. Wacht totdat de achtergrondtaak alle binnenkomende berichten heeft gelezen.

Tijdens een bidirectionele streaming-oproep kunnen de client en service op elk gewenst moment berichten naar elkaar verzenden. De beste clientlogica voor interactie met een bidirectionele aanroep is afhankelijk van de servicelogica.

Het type dat wordt geretourneerd van het starten van een bidirectionele streaming-aanroep implementeert IDisposable. Verwijder altijd een streaming-aanroep om ervoor te zorgen dat deze wordt gestopt en alle resources worden opgeschoond.

GRPC-headers openen

gRPC-aanroepen retourneren antwoordkopteksten. HTTP-antwoordheaders geven metagegevens van naam/waarde door over een aanroep die niet is gerelateerd aan het geretourneerde bericht.

Headers zijn toegankelijk met behulp van ResponseHeadersAsync, waarmee een verzameling metagegevens wordt geretourneerd. Headers worden meestal met het antwoordbericht geretourneerd; daarom moet u op ze wachten.

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 gebruik:

  • Moet wachten op het resultaat van ResponseHeadersAsync om de verzameling headers op te halen.
  • Het hoeft niet te worden benaderd voor ResponseAsync (of de reactiestroom bij streaming). Als er een antwoord is gegeven, retourneert ResponseHeadersAsync direct de header-gegevens.
  • Genereert een uitzondering als er een verbindings- of serverfout is opgetreden en headers niet zijn geretourneerd voor de gRPC-aanroep.

Toegang tot gRPC-trailers

gRPC-aanroepen kunnen antwoordtrailers retourneren. Trailers worden gebruikt om metagegevens van naam/waarde over een aanroep op te geven. Trailers bieden vergelijkbare functionaliteit voor HTTP-headers, maar worden aan het einde van de aanroep ontvangen.

Trailers zijn toegankelijk met behulp van GetTrailers(), die een verzameling metagegevens retourneert. Trailers worden geretourneerd nadat het antwoord is voltooid. Daarom moet u wachten op alle antwoordberichten voordat u toegang krijgt tot de trailers.

Unary- en clientstreaming-aanroepen moeten wachten op ResponseAsync voordat GetTrailers()wordt aangeroepen:

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- en bidirectionele streaming-aanroepen moeten klaar zijn met wachten op de antwoordstroom voordat u GetTrailers()aanroept:

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

Trailers zijn ook toegankelijk vanaf RpcException. Een service kan trailers retourneren samen met een niet-OK gRPC-status. In deze situatie worden de trailers opgehaald uit de uitzondering die wordt gegenereerd door de gRPC-client:

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

Deadline instellen

Het configureren van een gRPC-oproepdeadline wordt aanbevolen omdat deze een bovengrens biedt voor hoe lang een oproep kan worden uitgevoerd. Het stopt probleemservices die continu draaien en serverbronnen uitputten. Deadlines zijn een handig hulpmiddel voor het bouwen van betrouwbare apps.

Configureer CallOptions.Deadline om een deadline in te stellen voor een gRPC-aanroep:

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

Voor meer informatie, zie Betrouwbare gRPC-services met deadlines en annulering.

Aanvullende informatiebronnen