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

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Upozorňující

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v tématu .NET a .NET Core Zásady podpory. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

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í verzi najdete ve verzi .NET 8 tohoto článku.

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.
  • Volání gRPC pro unární, serverové streamování, streamování klientů a obousměrné metody 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. HttpClient Například použitý k volání, maximální velikost zprávy pro odesílání a příjem zpráv a protokolování lze zadat 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é nasazení 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í s kanály. 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 klientské továrny gRPC. Integrace gRPC s HttpClientFactory nabídkou centralizované alternativy k vytváření klientů gRPC.

Poznámka:

Volání gRPC přes HTTP/2 se Grpc.Net.Client v současné době v Xamarinu nepodporuje. Pracujeme na vylepšení podpory PROTOKOLU HTTP/2 v budoucí verzi Xamarinu. Grpc.Core a gRPC-Web jsou přijatelné alternativy, které dnes fungují.

Volání gRPC

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. Existují například GreeterClient dva způsoby volání SayHello:

  • GreeterClient.SayHelloAsync – volá Greeter.SayHello službu asynchronně. Můžete očekávat.
  • GreeterClient.SayHello – volá Greeter.SayHello službu a bloky do dokončení. Nepoužívejte v asynchronním kódu.

Volání streamování serveru

Volání streamování serveru začíná klientem, který odesílá zprávu požadavku. ResponseStream.MoveNext() přečte zprávy streamované ze služby. Volání streamování serveru je dokončeno při ResponseStream.MoveNext() vrácení 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ý spuštěním volání streamování serveru implementuje IDisposable. Vždy odstraňte volání streamování, abyste zajistili, že je zastavený a všechny prostředky se vyčistí.

Volání streamování klienta

Volání streamování klienta se spustí 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 se měl zavolat klient, 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 vrácený spuštěním volání streamování klienta implementuje IDisposable. Vždy odstraňte volání streamování, abyste zajistili, že je zastavený a všechny prostředky se vyčistí.

Obousměrné volání streamování

Obousměrné volání streamování se spustí bez odeslání zprávy klientem. 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é volání streamování je dokončeno, když ResponseStream už žádné zprávy neobsahují.

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 bez problémů dokončit obousměrná volání streamování. 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ě. Ve volání klient:

  1. Spustí nové obousměrné volání streamování voláním 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 volání streamování může klient a služba kdykoli odesí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 vrácený spuštěním obousměrného volání streamování implementuje IDisposable. Vždy odstraňte volání streamování, abyste zajistili, že je zastavený a všechny prostředky se vyčistí.

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 se obvykle vrací se zprávou odpovědi; proto je musíte 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 zvyk:

  • Musí čekat na výsledek ResponseHeadersAsync získání kolekce hlaviček.
  • Před streamováním není potřeba přistupovat ResponseAsync (ani ke streamu odpovědí). Pokud se vrátí odpověď, ResponseHeadersAsync okamžitě vrátí hlavičky.
  • Vyvolá výjimku, pokud došlo k chybě připojení nebo serveru a hlavičky nebyly vráceny pro volání gRPC.

Přístupové přívěsy gRPC

Volání gRPC mohou vracet přívěsy odpovědí. Přívěsy se používají k zadání metadat názvu a hodnoty volání. Přívěsy poskytují podobné funkce jako hlavičky HTTP, ale přijímají se 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 očekávat všechny zprávy s odpovědí.

Před voláním GetTrailers()musí čekat ResponseAsync unární volání a volání streamování klienta:

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 se přívěsy načítají 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. Zastaví chybné chování služeb v provozu navždy a vyčerpává prostředky serveru. Konečné termíny jsou užitečným nástrojem pro vytváření spolehlivých aplikací.

Nakonfigurujte CallOptions.Deadline nastavení konečného termínu 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