Sdílet prostřednictvím


Volání služeb gRPC pomocí klienta .NET

Poznámka:

Toto není nejnovější verze tohoto článku. Pro aktuální vydání si přečtěte verzi tohoto článku pro .NET 9.

Varování

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Aktuální verzi tohoto článku najdete v verzi .NET 9.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální vydání si přečtěte v článku o verzi .NET 9.

Klientská knihovna .NET gRPC je k dispozici v balíčku NuGet Grpc.Net.Client . Tento dokument vysvětluje, jak:

  • Nakonfigurujte klienta gRPC tak, aby volal služby gRPC.
  • Provádět gRPC volání na unární, serverové streamování, klientské streamování a obousměrné streamování.

Konfigurace klienta gRPC

Klienti gRPC jsou konkrétní typy klientů, kteří se generují ze souborů .proto. Konkrétní klient gRPC obsahuje metody, které se překládají na službu gRPC v souboru .proto. Například volání Greeter služby generuje GreeterClient typ s metodami volání služby.

Klient gRPC se vytvoří z kanálu. Začněte tím, že použijete GrpcChannel.ForAddress k vytvoření kanálu a pak pomocí kanálu vytvoříte klienta gRPC:

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

Kanál představuje dlouhodobé připojení ke službě gRPC. Když se kanál vytvoří, nakonfiguruje se s možnostmi souvisejícími s voláním služby. Například HttpClient použitý k volání, maximální velikost odesílaných a přijímaných zpráv a protokolování lze zadat na GrpcChannelOptions a použít s GrpcChannel.ForAddress. Úplný seznam možností najdete v tématu Možnosti konfigurace klienta.

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

Konfigurace TLS

Klient gRPC musí používat stejné zabezpečení na úrovni připojení jako volaná služba. GRPC client Transport Layer Security (TLS) se konfiguruje při vytvoření kanálu gRPC. Klient gRPC vyvolá chybu při volání služby a zabezpečení na úrovni připojení kanálu a služby se neshodují.

Pokud chcete nakonfigurovat kanál gRPC tak, aby používal protokol TLS, ujistěte se, že adresa serveru začíná httpsna . Například GrpcChannel.ForAddress("https://localhost:5001") používá protokol HTTPS. Kanál gRPC automaticky vyjedná připojení zabezpečené protokolem TLS a používá zabezpečené připojení k volání gRPC.

Tip

gRPC podporuje ověřování klientských certifikátů přes protokol TLS. Informace o konfiguraci klientských certifikátů pomocí kanálu gRPC najdete v tématu Ověřování a autorizace v gRPC pro ASP.NET Core.

Pokud chcete volat nezabezpečené služby gRPC, ujistěte se, že adresa serveru začíná httpna . Například GrpcChannel.ForAddress("http://localhost:5000") používá protokol HTTP. V .NET Core 3.1 je potřeba další konfigurace pro volání nezabezpečených služeb gRPC pomocí klienta .NET.

Výkon klienta

Výkon a využití kanálů a klientů:

  • Vytvoření kanálu může být nákladná operace. Opětovné použití kanálu pro volání gRPC poskytuje výhody výkonu.
  • Kanál spravuje připojení k serveru. Pokud je připojení zavřené nebo ztracené, kanál se při příštím volání gRPC automaticky znovu připojí.
  • Klienti gRPC se vytvářejí pomocí kanálů. Klienti gRPC jsou jednoduché objekty a není nutné je ukládat do mezipaměti ani opakovaně používat.
  • Z kanálu lze vytvořit více klientů gRPC, včetně různých typů klientů.
  • Kanál a klienti vytvořené z kanálu můžou bezpečně používat více vláken.
  • Klienti vytvořená z kanálu můžou provádět více souběžných volání.

GrpcChannel.ForAddress není jedinou možností pro vytvoření klienta gRPC. Pokud voláte služby gRPC z aplikace ASP.NET Core, zvažte integraci továrny klientů gRPC. Integrace gRPC s HttpClientFactory nabízí centralizovanou alternativu k vytváření klientů gRPC.

Při volání metod gRPC preferujte použití asynchronní programování s asynchronní a await. Provádění volání gRPC s blokováním, jako je použití Task.Result nebo Task.Wait(), zabraňuje jiným úlohám v používání vlákna. To může vést k hladovění fondu vláken a nízkému výkonu. Mohlo by to způsobit, že aplikace přestane reagovat kvůli mrtvému zámku. Další informace najdete v tématu asynchronní volání v klientských aplikacích.

Vytvořte gRPC volání

Volání gRPC je zahájeno voláním metody na klientovi. Klient gRPC zpracuje serializaci zpráv a adresuje volání gRPC správné službě.

gRPC má různé typy metod. Způsob použití klienta k volání gRPC závisí na typu volané metody. Typy metod gRPC jsou:

  • Unární
  • Streamování serveru
  • Streamování klienta
  • Obousměrné streamování

Unární hovor

Unární hovor začíná klientem, který odesílá zprávu požadavku. Po dokončení služby se vrátí zpráva odpovědi.

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

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

Každá unární metoda služby v .proto souboru bude mít za následek dvě metody .NET pro konkrétní typ klienta gRPC pro volání metody: asynchronní metodu a blokující metodu. Například na GreeterClient existují dva způsoby volání SayHello:

  • GreeterClient.SayHelloAsync – asynchronně volá službu Greeter.SayHello. Lze očekávat.
  • GreeterClient.SayHello – volá službu Greeter.SayHello a blokuje do dokončení. Nepoužívejte v asynchronním kódu. Může způsobit problémy s výkonem a spolehlivostí.

Další informace najdete v tématu asynchronní volání v klientských aplikacích.

Streamovací volání serveru

Volání serverového streamování začíná tím, že klient odešle žádost. ResponseStream.MoveNext() přečte zprávy streamované ze služby. Volání streamování serveru je dokončeno, když ResponseStream.MoveNext() vrátí 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
}

Při použití jazyka C# 8 nebo novější await foreach je možné syntaxi použít ke čtení zpráv. Metoda IAsyncStreamReader<T>.ReadAllAsync() rozšíření čte všechny zprávy ze streamu odpovědí:

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
}

Typ vrácený zahájením serverového streamovacího volání implementuje IDisposable. Vždy uvolněte streamovací volání, abyste zajistili jeho zastavení a vyčištění všech prostředků.

Streamovaná volání klienta

Streamovací volání klienta začne bez odeslání zprávy klientem. Klient se může rozhodnout odesílat zprávy pomocí RequestStream.WriteAsync. Po dokončení odesílání zpráv by měl klient zavolat RequestStream.CompleteAsync(), aby službu informoval. Volání se dokončí, když služba vrátí zprávu s odpovědí.

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

Typ, který je vrácen při spuštění klientského streamovacího volání, implementuje IDisposable. Vždy uvolněte streamovací volání, abyste zajistili, že je zastaveno a všechny prostředky jsou vyčištěny.

Obousměrné streamovací volání

Obousměrné streamovací volání se zahájí aniž by klient poslal zprávu. Klient se může rozhodnout odesílat zprávy pomocí RequestStream.WriteAsync. Zprávy streamované ze služby jsou přístupné pomocí ResponseStream.MoveNext() nebo ResponseStream.ReadAllAsync(). Obousměrný streamingový hovor je dokončen, když ResponseStream už nemá žádné zprávy.

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;

Pokud chcete dosáhnout nejlepšího výkonu a vyhnout se zbytečným chybám v klientovi a službě, zkuste obousměrná volání streamování hladce dokončit. Obousměrné volání se dokončí bezútěšně, když server dokončí čtení streamu požadavku a klient dokončil čtení streamu odpovědí. Předchozí ukázkové volání je jedním z příkladů obousměrného volání, které končí elegantně. Během hovoru klient:

  1. Začíná nové obousměrné streamovací volání pomocí volání EchoClient.Echo.
  2. Vytvoří úlohu na pozadí pro čtení zpráv ze služby pomocí ResponseStream.ReadAllAsync().
  3. Odesílá zprávy na server pomocí RequestStream.WriteAsync.
  4. Upozorní server, se kterým dokončil odesílání zpráv .RequestStream.CompleteAsync()
  5. Čeká, dokud úloha na pozadí nepřečte všechny příchozí zprávy.

Během obousměrného streamovacího hovoru mohou klient i služba kdykoli posílat zprávy. Nejlepší logika klienta pro interakci s obousměrným voláním se liší v závislosti na logice služby.

Typ, který je výsledkem při spuštění obousměrného streamovacího volání, implementuje IDisposable. Vždy uvolněte streamovací volání, aby bylo zajištěno, že je zastaveno a všechny prostředky jsou vyčištěny.

Přístup k hlavičce gRPC

Volání gRPC vrací hlavičky odpovědi. Hlavičky odpovědi HTTP předávají metadata názvu a hodnoty volání, které nesouvisí s vrácenou zprávou.

Záhlaví jsou přístupná pomocí ResponseHeadersAsync, která vrací kolekci metadat. Hlavičky jsou obvykle vráceny se zprávou odpovědi; musíte je proto očekávat.

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 použití:

  • Je třeba počkat na výsledek ResponseHeadersAsync, aby bylo možné získat kolekci hlaviček.
  • Před streamováním není potřeba přistupovat ResponseAsync (ani ke streamu odpovědí). Pokud byla odpověď vrácena, ResponseHeadersAsync okamžitě vrátí hlavičky.
  • Vyvolá výjimku, pokud došlo k chybě připojení nebo serveru a hlavičky nebyly poskytnuty pro volání gRPC.

Získání gRPC trailerů

Volání gRPC mohou vracet trailery odpovědí. Trailery se používají k poskytování metadat s názvy a hodnotami o volání. Trailery poskytují podobnou funkčnost jako hlavičky HTTP, ale jsou přijímány na konci volání.

Přívěsy jsou přístupné pomocí GetTrailers(), které vrací kolekci metadat. Přívěsy se vrátí po dokončení odpovědi. Proto musíte před přístupem k přívěsům počkat na všechny odpovědi.

Unární a klientská streamová volání musí čekat ResponseAsync před voláním 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");

Volání serveru a obousměrného streamování musí před voláním GetTrailers() dokončit čekání na stream odpovědí.

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

Přívěsy jsou také přístupné z RpcException. Služba může vracet přívěsy spolu se stavem non-OK gRPC. V této situaci jsou metadata získávána z výjimky vyvolané klientem 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");
}

Konfigurace konečného termínu

Konfigurace konečného termínu volání gRPC se doporučuje, protože poskytuje horní limit, jak dlouho může volání běžet. Zabraňuje nesprávně fungujícím službám běžet nekonečně dlouho, čímž brání vyčerpávání prostředků serveru. Konečné termíny jsou užitečným nástrojem pro vytváření spolehlivých aplikací.

Nakonfigurujte CallOptions.Deadline, abyste nastavili termín pro volání 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.");
}

Další informace najdete v tématu Spolehlivé služby gRPC s termíny a zrušením.

Další materiály