Chamar serviços gRPC com o cliente .NET
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.
Advertência
Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.
Importante
Estas informações referem-se a um produto de pré-lançamento que pode ser substancialmente modificado antes de ser lançado comercialmente. A Microsoft não oferece garantias, expressas ou implícitas, em relação às informações fornecidas aqui.
Para a versão atual, consulte a versão .NET 9 deste artigo.
Uma biblioteca de cliente gRPC .NET está disponível no pacote Grpc.Net.Client NuGet. Este documento explica como:
- Configure um cliente gRPC para chamar serviços gRPC.
- Faça chamadas gRPC para métodos de streaming unário, de servidor, de cliente e bidirecional.
Configurar o cliente gRPC
Os clientes gRPC são tipos de clientes concretos que são gerados a partir de arquivos .proto
. O cliente gRPC concreto tem métodos que correspondem ao serviço gRPC no arquivo .proto
. Por exemplo, um serviço chamado Greeter
gera um tipo de GreeterClient
com métodos para chamar o serviço.
Um cliente gRPC é criado a partir de um canal. Comece usando GrpcChannel.ForAddress
para criar um canal e, em seguida, use o canal para criar um cliente gRPC:
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greet.GreeterClient(channel);
Um canal representa uma conexão de longa duração com um serviço gRPC. Quando um canal é criado, ele é configurado com opções relacionadas à chamada de um serviço. Por exemplo, o HttpClient
usado para fazer chamadas, o tamanho máximo de enviar e receber mensagens e o registro em log podem ser especificados no GrpcChannelOptions
e usados com GrpcChannel.ForAddress
. Para obter uma lista completa de opções, consulte opções de configuração do cliente.
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
Configurar TLS
Um cliente gRPC deve usar a mesma segurança de nível de conexão que o serviço chamado. O TLS (Transport Layer Security) do cliente gRPC é configurado quando o canal gRPC é criado. Um cliente gRPC gera um erro quando chama um serviço e a segurança no nível de conexão entre o canal e o serviço não corresponde.
Para configurar um canal gRPC para usar TLS, verifique se o endereço do servidor começa com https
. Por exemplo, GrpcChannel.ForAddress("https://localhost:5001")
usa o protocolo HTTPS. O canal gRPC negocia automaticamente uma conexão protegida por TLS e usa uma conexão segura para fazer chamadas gRPC.
Dica
O gRPC suporta autenticação de certificado de cliente por TLS. Para obter informações sobre como configurar certificados de cliente com um canal gRPC, consulte Authentication and authorization in gRPC for ASP.NET Core.
Para chamar serviços gRPC não seguros, verifique se o endereço do servidor começa com http
. Por exemplo, GrpcChannel.ForAddress("http://localhost:5000")
usa o protocolo HTTP. No .NET Core 3.1, é necessária uma configuração adicional para chamar serviços gRPC inseguros com o cliente .NET.
Desempenho do cliente
Desempenho e uso do canal e do cliente:
- Criar um canal pode ser uma operação cara. A reutilização de um canal para chamadas gRPC oferece benefícios de desempenho.
- Um canal gerencia conexões com o servidor. Se a conexão for fechada ou perdida, o canal se reconecta automaticamente na próxima vez que uma chamada gRPC for feita.
- Os clientes gRPC são criados com canais. Os clientes gRPC são objetos leves e não precisam ser armazenados em cache ou reutilizados.
- Vários clientes gRPC podem ser criados a partir de um canal, incluindo diferentes tipos de clientes.
- Um canal e clientes criados a partir do canal podem ser usados com segurança por vários threads.
- Os clientes criados a partir do canal podem fazer várias chamadas simultâneas.
GrpcChannel.ForAddress
não é a única opção para criar um cliente gRPC. Se estiver chamando serviços gRPC de um aplicativo ASP.NET Core, considere de integração de fábrica do cliente gRPC . A integração do gRPC com o HttpClientFactory
oferece uma alternativa centralizada à criação de clientes gRPC.
Fazer chamadas gRPC
Uma chamada gRPC é iniciada chamando um método no cliente. O cliente gRPC manipulará a serialização de mensagens e endereçará a chamada gRPC para o serviço correto.
gRPC tem diferentes tipos de métodos. Como o cliente é usado para fazer uma chamada gRPC depende do tipo de método chamado. Os tipos de método gRPC são:
- Unário
- Streaming de servidor
- Transmissão para cliente
- Streaming bidirecional
Chamada unária
Uma chamada unária começa com o cliente enviando uma mensagem de solicitação. Uma mensagem de resposta é retornada quando o serviço é concluído.
var client = new Greet.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });
Console.WriteLine("Greeting: " + response.Message);
// Greeting: Hello World
Cada método de serviço unário no arquivo .proto
resultará em dois métodos .NET no tipo de cliente gRPC concreto para chamar o método: um método assíncrono e um método de bloqueio. Por exemplo, em GreeterClient
há duas maneiras de chamar SayHello
:
-
GreeterClient.SayHelloAsync
- chama o serviçoGreeter.SayHello
de forma assíncrona. Pode ser aguardado. -
GreeterClient.SayHello
- chama o serviçoGreeter.SayHello
e bloqueia até completar. Não use em código assíncrono.
Chamada de streaming do servidor
Uma chamada de streaming de servidor começa com o cliente enviando uma mensagem de solicitação.
ResponseStream.MoveNext()
lê mensagens transmitidas do serviço. A chamada de streaming do servidor é concluída quando ResponseStream.MoveNext()
retorna 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
}
Ao usar C# 8 ou posterior, a sintaxe await foreach
pode ser usada para ler mensagens. O método de extensão IAsyncStreamReader<T>.ReadAllAsync()
lê todas as mensagens do fluxo de resposta:
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
}
O tipo retornado ao iniciar uma chamada de streaming de servidor implementa IDisposable
. Sempre descarte uma chamada de streaming para garantir que ela seja interrompida e que todos os recursos sejam limpos.
Chamada de streaming do cliente
Uma chamada de streaming do cliente é iniciada sem o cliente enviar uma mensagem. O cliente pode optar por enviar mensagens com RequestStream.WriteAsync
. Quando o cliente terminar de enviar mensagens, RequestStream.CompleteAsync()
deve ser chamado para notificar o serviço. A chamada é concluída quando o serviço retorna uma mensagem de resposta.
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
O tipo retornado do início de uma chamada de streaming do cliente implementa IDisposable
. Sempre descarte uma chamada de streaming para garantir que ela seja interrompida e que todos os recursos sejam limpos.
Chamada de streaming bidirecional
Uma chamada de streaming bidirecional começa sem o cliente enviar uma mensagem. O cliente pode optar por enviar mensagens com RequestStream.WriteAsync
. As mensagens transmitidas a partir do serviço podem ser acessadas com ResponseStream.MoveNext()
ou ResponseStream.ReadAllAsync()
. A chamada de streaming bidirecional é concluída quando o ResponseStream
não tem mais mensagens.
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;
Para obter o melhor desempenho e evitar erros desnecessários no cliente e no serviço, tente concluir chamadas de streaming bidirecionais graciosamente. Uma chamada bidirecional é concluída normalmente quando o servidor termina de ler o fluxo de solicitação e o cliente termina de ler o fluxo de resposta. A chamada de exemplo anterior é um exemplo de uma chamada bidirecional que termina graciosamente. Na chamada, o cliente:
- Inicia uma nova chamada de streaming bidirecional chamando
EchoClient.Echo
. - Cria uma tarefa em segundo plano para ler mensagens do serviço usando
ResponseStream.ReadAllAsync()
. - Envia mensagens para o servidor com
RequestStream.WriteAsync
. - Notifica o servidor que terminou de enviar mensagens com
RequestStream.CompleteAsync()
. - Aguarda até que a tarefa em segundo plano tenha lido todas as mensagens recebidas.
Durante uma chamada de streaming bidirecional, o cliente e o serviço podem enviar mensagens um para o outro a qualquer momento. A melhor lógica do cliente para interagir com uma chamada bidirecional varia dependendo da lógica do serviço.
O tipo devolvido ao iniciar uma chamada de streaming bidirecional implementa IDisposable
. Sempre descarte uma chamada de streaming para garantir que ela seja interrompida e que todos os recursos sejam limpos.
Acessar cabeçalhos gRPC
As chamadas gRPC retornam cabeçalhos de resposta. Os cabeçalhos de resposta HTTP passam metadados de nome/valor sobre uma chamada que não está relacionada à mensagem retornada.
Os cabeçalhos podem ser acessados usando ResponseHeadersAsync
, que retorna uma coleção de metadados. Os cabeçalhos normalmente são retornados com a mensagem de resposta; portanto, você deve aguardá-los.
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;
Utilização do ResponseHeadersAsync
:
- Deve aguardar o resultado de
ResponseHeadersAsync
para obter a coleção de cabeçalhos. - Não precisa ser acessado antes
ResponseAsync
(ou o fluxo de resposta durante o streaming). Se uma resposta tiver sido retornada,ResponseHeadersAsync
retornará cabeçalhos instantaneamente. - Lançará uma exceção se ocorrer um erro de conexão ou de servidor e, como resultado, os cabeçalhos não forem retornados para a chamada gRPC.
Aceda às metainformações gRPC
As chamadas gRPC podem retornar trailers de resposta. Os trailers são usados para fornecer metadados de nome-valor relativamente a uma chamada. Os trailers fornecem funcionalidade semelhante aos cabeçalhos HTTP, mas são recebidos no final da chamada.
Os trailers podem ser acessados usando GetTrailers()
, que retorna uma coleção de metadados. Os trailers são devolvidos após a conclusão da resposta. Portanto, você deve aguardar todas as mensagens de resposta antes de acessar os trailers.
Chamadas de streaming unárias e de cliente devem aguardar ResponseAsync
antes de ligar para 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");
As chamadas de streaming bidirecionais e de servidor devem terminar de aguardar o fluxo de resposta antes de chamar 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");
Os trailers de vídeo também são acessíveis a partir de RpcException
. Um serviço pode devolver reboques juntamente com um estado gRPC não OK. Nessa situação, os trailers são recuperados da exceção lançada pelo cliente 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");
}
Configurar prazo
A configuração de um prazo de chamada gRPC é recomendada porque fornece um limite superior de quanto tempo uma chamada pode ser executada. Ele impede que serviços com comportamento incorreto sejam executados para sempre e esgotem os recursos do servidor. Os prazos são uma ferramenta útil para criar aplicações fiáveis.
Configure CallOptions.Deadline
para definir um limite de tempo para uma chamada 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.");
}
Para obter mais informações, consulte Serviços gRPC confiáveis com prazos e cancelamento.