Serviços gRPC confiáveis com prazos finais e cancelamento
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.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, confira .NET e a Política de Suporte do .NET Core. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Importante
Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.
Para a versão atual, consulte a versão .NET 9 deste artigo.
Prazos e cancelamento são recursos usados por clientes gRPC para anular chamadas em andamento. Este artigo discute por que os prazos e o cancelamento são importantes e como usá-los em aplicativos gRPC do .NET.
Prazos
Um prazo permite que um cliente gRPC especifique quanto tempo aguardará a conclusão de uma chamada. Quando um prazo é excedido, a chamada é cancelada. A definição de um prazo é importante porque fornece o tempo máximo de execução de uma chamada. Ela impede que serviços funcionando de forma inadequada sejam executados para sempre e o esgotamento dos recursos do servidor. Os prazos são uma ferramenta útil para BLD aplicativos confiáveis e devem ser configurados.
Configuração do prazo:
- Um prazo é configurado usando
CallOptions.Deadline
quando uma chamada é feita. - Não há valor de prazo padrão. As chamadas gRPC não possuem limite de tempo, a menos que um prazo seja especificado.
- Um prazo é a hora UTC de quando o prazo é excedido. Por exemplo,
DateTime.UtcNow.AddSeconds(5)
é um prazo de cinco segundos a partir de agora. - Se uma hora passada ou atual for usada, a chamada excederá imediatamente o prazo.
- O prazo é enviado com a chamada gRPC para o serviço e é rastreado independentemente pelo cliente e pelo serviço. É possível que uma chamada gRPC seja concluída em um computador, mas no momento em que a resposta retornou ao cliente, o prazo foi excedido.
Se um prazo for excedido, o cliente e o serviço terão um comportamento diferente:
- O cliente anula imediatamente a solicitação HTTP subjacente e gera um erro
DeadlineExceeded
. O aplicativo cliente pode capturar o erro e exibir uma mensagem de tempo limite para o usuário. - No servidor, a solicitação HTTP em execução é anulada e ServerCallContext.CancellationToken é gerado. Embora a solicitação HTTP seja anulada, a chamada gRPC continuará sendo executada no servidor até que o método seja concluído. É importante que o token de cancelamento seja passado para métodos assíncronos para que eles sejam cancelados junto com a chamada. Por exemplo, passar um token de cancelamento para consultas de banco de dados assíncronas e solicitações HTTP. Passar um token de cancelamento permite que a chamada cancelada seja concluída rapidamente no servidor e libere recursos para outras chamadas.
Configure CallOptions.Deadline
para definir um prazo 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.");
}
Usando ServerCallContext.CancellationToken
em um serviço gRPC:
public override async Task<HelloReply> SayHello(HelloRequest request,
ServerCallContext context)
{
var user = await _databaseContext.GetUserAsync(request.Name,
context.CancellationToken);
return new HelloReply { Message = "Hello " + user.DisplayName };
}
Prazos e novas tentativas
Quando uma chamada gRPC é configurada com tratamento de falha de repetição e um prazo, o prazo acompanha o tempo em todas as tentativas de uma chamada gRPC. Se o prazo for excedido, uma chamada gRPC anulará imediatamente a solicitação HTTP subjacente, ignorará todas as tentativas restantes e gerará um erro DeadlineExceeded
.
Propagando prazos
Quando uma chamada gRPC é feita de um serviço gRPC em execução, o prazo deve ser propagado. Por exemplo:
- Chamadas de aplicativo cliente
FrontendService.GetUser
com um prazo. FrontendService
chamaUserService.GetUser
. O prazo especificado pelo cliente deve ser especificado com a nova chamada gRPC.UserService.GetUser
recebe o prazo. Ele atingirá o tempo limite corretamente se o prazo do aplicativo cliente for excedido.
O contexto de chamada fornece o prazo com ServerCallContext.Deadline
:
public override async Task<UserResponse> GetUser(UserRequest request,
ServerCallContext context)
{
var client = new User.UserServiceClient(_channel);
var response = await client.GetUserAsync(
new UserRequest { Id = request.Id },
deadline: context.Deadline);
return response;
}
A propagação manual de prazos pode ser complicada. O prazo precisa ser passado para cada chamada, e é fácil ignorar acidentalmente. Uma solução automática está disponível com a fábrica de clientes gRPC. Especificando EnableCallContextPropagation
:
- Propaga automaticamente o token de prazo e cancelamento para chamadas filho.
- Não propaga o prazo se a chamada filho especificar um prazo menor. Por exemplo, um prazo propagado de 10 segundos não será usado se uma chamada filho especificar um novo prazo de 5 segundos usando
CallOptions.Deadline
. Quando vários prazos estão disponíveis, o menor prazo é usado. - É uma excelente maneira de garantir que cenários gRPC complexos e aninhados sempre propaguem o prazo final e o cancelamento.
services
.AddGrpcClient<User.UserServiceClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
})
.EnableCallContextPropagation();
Para obter mais informações, consulte Integração de fábrica do cliente gRPC no .NET.
Cancelamento
O cancelamento permite que um cliente gRPC cancele chamadas de execução prolongada que não são mais necessárias. Por exemplo, uma chamada gRPC que transmite atualizações em tempo real é iniciada quando o usuário acessa uma página em um site. O fluxo deve ser cancelado quando o usuário navega para longe da página.
Uma chamada gRPC pode ser cancelada no cliente passando um token de cancelamento com CallOptions.CancellationToken ou chamando Dispose
na chamada.
private AsyncServerStreamingCall<HelloReply> _call;
public void StartStream()
{
_call = client.SayHellos(new HelloRequest { Name = "World" });
// Read response in background task.
_ = Task.Run(async () =>
{
await foreach (var response in _call.ResponseStream.ReadAllAsync())
{
Console.WriteLine("Greeting: " + response.Message);
}
});
}
public void StopStream()
{
_call.Dispose();
}
Os serviços gRPC que podem ser cancelados devem:
- Passar
ServerCallContext.CancellationToken
para métodos assíncronos. O cancelamento de métodos assíncronos permite que a chamada no servidor seja concluída rapidamente. - Propagar o token de cancelamento para chamadas filho. A propagação do token de cancelamento garante que as chamadas filho sejam canceladas com o pai. Fábrica do cliente gRPC e
EnableCallContextPropagation()
propaga automaticamente o token de cancelamento.