Como usar o Cliente gRPC no Delphi

· Componentes

O componente TsgcGRPCClient traz o gRPC para o Delphi e o C++Builder sem nenhum runtime externo. Ele faz parte da edição Enterprise do sgcWebSockets e roda em Windows, macOS, Linux, iOS e Android. Este guia explica como o cliente funciona, como configurá-lo e como fazer cada um dos quatro tipos de chamada gRPC a partir do Delphi.

Como funciona

O gRPC são mensagens Protocol Buffers enquadradas sobre HTTP/2. Cada mensagem é enviada como um payload prefixado por tamanho em um stream HTTP/2, a requisição carrega headers como content-type: application/grpc e grpc-timeout, e o status final chega como os trailers grpc-status e grpc-message.

O sgcWebSockets já inclui uma pilha HTTP/2 completa, portanto o TsgcGRPCClient fica sobre um transporte TsgcHTTP2Client. Você fornece os bytes da requisição serializada, ele faz o enquadramento, os headers e o trabalho de timeout, abre o stream HTTP/2 e analisa a resposta e os trailers de volta em um TsgcGRPCResponse tipado. Como o cliente trabalha com TBytes brutos, você pode combiná-lo com qualquer biblioteca de Protocol Buffers que já utilize.

Configurando o cliente

Um canal gRPC é uma conexão HTTP/2. Crie um TsgcHTTP2Client, aponte-o para o host e a porta, e em seguida atribua-o à propriedade Client do componente gRPC. Para TLS, o cliente HTTP/2 usa OpenSSL ou o SChannel do Windows; para um servidor local em texto puro (h2c), defina TLS como False.

uses
  sgcHTTP2, sgcGRPC_Client, sgcGRPC_Classes, sgcGRPC_Types;

var
  HTTP2: TsgcHTTP2Client;
  GRPC: TsgcGRPCClient;
begin
  HTTP2 := TsgcHTTP2Client.Create(nil);
  HTTP2.Host := 'grpc.example.com';
  HTTP2.Port := 443;
  HTTP2.TLS  := True;                 // False for a local h2c server
  HTTP2.TLSOptions.IOHandler := iohOpenSSL;  // or iohSChannel on Windows

  GRPC := TsgcGRPCClient.Create(nil);
  GRPC.Client := HTTP2;
end;

Configurando o canal

As configurações que valem para todo o canal ficam em ChannelOptions. Você pode ativar a compressão gzip por mensagem, escolher o content type da transmissão e elevar os limites de tamanho de mensagem e de metadata.

// gzip-compress outgoing messages
GRPC.ChannelOptions.Compression := grpcGzip;          // grpcNoCompression, grpcGzip, grpcDeflate, grpcSnappy

// application/grpc+proto (default) or application/grpc+json
GRPC.ChannelOptions.ContentType := grpcProto;         // or grpcJSON

// raise the limits for large messages
GRPC.ChannelOptions.MaxMessageSize  := 16 * 1024 * 1024;
GRPC.ChannelOptions.MaxMetadataSize := 64 * 1024;

Metadata e autenticação

Headers personalizados viajam como metadata do gRPC. Adicione-os a DefaultMetadata para enviá-los em todas as chamadas, que é o lugar habitual para um token de autorização ou um header de rastreamento.

GRPC.DefaultMetadata.Add('authorization', 'Bearer eyJ...');
GRPC.DefaultMetadata.Add('x-api-key', 'my-api-key');

Deadlines e cancelamento

Um timeout por chamada é enviado como o header padrão grpc-timeout, de modo que o servidor possa desistir no mesmo momento que o seu cliente. Uma chamada de streaming em andamento pode ser interrompida a qualquer momento com CancelCall, passando o id do stream retornado quando o stream foi aberto.

Chamadas unárias

Uma chamada unária envia uma requisição e recebe uma resposta. Call bloqueia até a resposta chegar e retorna um TsgcGRPCResponse com o StatusCode, o StatusMessage, os bytes brutos de Data (ou DataString) e os Trailers.

var
  oResponse: TsgcGRPCResponse;
begin
  oResponse := GRPC.Call('helloworld.Greeter', 'SayHello', RequestBytes);
  if oResponse.StatusCode = grpcOK then
    Memo1.Text := oResponse.DataString
  else
    ShowMessage('gRPC error ' + oResponse.StatusMessage);
end;

Para manter a interface responsiva, use CallAsync. Ele retorna de imediato e entrega a resposta por meio do evento OnGRPCResponse.

GRPC.OnGRPCResponse := GRPCResponse;
GRPC.CallAsync('helloworld.Greeter', 'SayHello', RequestBytes);

procedure TForm1.GRPCResponse(Sender: TObject; const Response: TsgcGRPCResponse);
begin
  if Response.StatusCode = grpcOK then
    Memo1.Text := Response.DataString;
end;

Server streaming

Com o server streaming você envia uma requisição e o servidor retorna um stream de mensagens. Cada mensagem dispara OnGRPCStreamMessage, e OnGRPCStreamEnd dispara uma vez com o status final.

GRPC.OnGRPCStreamMessage := GRPCStreamMessage;
GRPC.OnGRPCStreamEnd := GRPCStreamEnd;

GRPC.ServerStreamingCall('chat.Feed', 'Subscribe', RequestBytes);

procedure TForm1.GRPCStreamMessage(Sender: TObject; aStreamId: Integer;
  const aData: TBytes);
begin
  Memo1.Lines.Add(DecodeMessage(aData));
end;

Client streaming

O client streaming é a imagem espelhada: você envia um stream de mensagens e o servidor responde uma vez. Abra o stream, envie cada mensagem e em seguida feche-o para ler a resposta única.

var
  vStreamId: Integer;
  oResponse: TsgcGRPCResponse;
begin
  vStreamId := GRPC.OpenClientStream('upload.Service', 'Send');
  GRPC.SendStreamMessage(vStreamId, ChunkBytes1);
  GRPC.SendStreamMessage(vStreamId, ChunkBytes2);
  oResponse := GRPC.CloseClientStream(vStreamId);
  if oResponse.StatusCode = grpcOK then
    ShowMessage('Upload accepted');
end;

Streaming bidirecional

O streaming bidirecional executa uma troca full-duplex sobre um único stream HTTP/2: ambos os lados enviam quando quiserem. Abra o stream, envie mensagens conforme necessário, trate as mensagens recebidas em OnGRPCStreamMessage e feche ao terminar.

var
  vStreamId: Integer;
begin
  vStreamId := GRPC.OpenBidiStream('chat.Room', 'Connect');
  GRPC.SendBidiMessage(vStreamId, EncodeMessage('hello'));
  // ... incoming messages arrive on OnGRPCStreamMessage ...
  GRPC.CloseBidiStream(vStreamId);
end;

Códigos de status e tratamento de erros

Toda resposta carrega um StatusCode tipado, de grpcOK ao longo do conjunto padrão do gRPC (grpcNOT_FOUND, grpcUNAVAILABLE, grpcDEADLINE_EXCEEDED, grpcUNAUTHENTICATED e os demais). Para chamadas assíncronas e de streaming, OnGRPCError reporta um status gRPC diferente de OK e OnGRPCException reporta uma falha de transporte ou de conexão.

GRPC.OnGRPCError := GRPCError;

procedure TForm1.GRPCError(Sender: TObject; aStreamId: Integer;
  aStatusCode: TsgcGRPCStatusCode; const aStatusMessage: string);
begin
  Memo1.Lines.Add(Format('gRPC error %d: %s', [Ord(aStatusCode), aStatusMessage]));
end;

Retry automático

O cliente pode repetir chamadas que falharam por você. Ative RetryPolicy, defina a quantidade de tentativas e o backoff, e liste os códigos de status que devem disparar um retry. Esta é a configuração típica para falhas transitórias de grpcUNAVAILABLE.

GRPC.RetryPolicy.Enabled := True;
GRPC.RetryPolicy.MaxAttempts := 4;
GRPC.RetryPolicy.InitialBackoff := 200;     // ms
GRPC.RetryPolicy.BackoffMultiplier := 2.0;
GRPC.RetryPolicy.RetryableStatusCodes :=
  GRPC.RetryPolicy.RetryableStatusCodes + [grpcUNAVAILABLE, grpcRESOURCE_EXHAUSTED];

Para lógica transversal, como logging ou renovação de um token, adicione uma entrada à cadeia de Interceptors, que envolve cada chamada em um único lugar.

Por onde seguir

Sobre este cliente genérico, o sgcWebSockets inclui clientes tipados para os serviços gRPC do Google Cloud (Pub/Sub, Speech-to-Text, Translation, Vision, Natural Language, Cloud Storage, BigQuery e Vertex AI), de modo que você chama métodos de alto nível em vez de montar protobufs manualmente. Um exemplo pronto para executar está em Demos\21.GRPC\01.GRPC_Client, e a referência completa está na página do produto Cliente gRPC.

Dúvidas ou ajuda para começar? Entre em contato. Você receberá uma resposta das pessoas que escreveram o código.