Cómo usar el cliente gRPC en Delphi

· Componentes

El componente TsgcGRPCClient lleva gRPC a Delphi y C++Builder sin ningún runtime externo. Forma parte de la edición Enterprise de sgcWebSockets y se ejecuta en Windows, macOS, Linux, iOS y Android. Esta guía explica cómo funciona el cliente, cómo configurarlo y cómo realizar cada uno de los cuatro tipos de llamada gRPC desde Delphi.

Cómo funciona

gRPC son mensajes de Protocol Buffers enmarcados sobre HTTP/2. Cada mensaje se envía como una carga útil con prefijo de longitud en un stream HTTP/2, la solicitud lleva cabeceras como content-type: application/grpc y grpc-timeout, y el estado final llega como los trailers grpc-status y grpc-message.

sgcWebSockets ya incluye una pila HTTP/2 completa, por lo que TsgcGRPCClient se asienta sobre un transporte TsgcHTTP2Client. Le proporcionas los bytes de tu solicitud serializada, este se encarga del enmarcado, las cabeceras y el tiempo de espera, abre el stream HTTP/2 y analiza la respuesta y los trailers de vuelta en un TsgcGRPCResponse tipado. Como el cliente trabaja con TBytes en bruto, puedes combinarlo con cualquier librería de Protocol Buffers que ya utilices.

Configurar el cliente

Un canal gRPC es una conexión HTTP/2. Crea un TsgcHTTP2Client, apúntalo al host y al puerto, y luego asígnalo a la propiedad Client del componente gRPC. Para TLS, el cliente HTTP/2 usa OpenSSL o Windows SChannel; para un servidor local en texto plano (h2c), establece TLS en 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;

Configurar el canal

Los ajustes que afectan a todo el canal están en ChannelOptions. Puedes activar la compresión gzip por mensaje, elegir el tipo de contenido del protocolo y aumentar los límites de tamaño de mensaje y de metadatos.

// 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;

Metadatos y autenticación

Las cabeceras personalizadas viajan como metadatos de gRPC. Añádelas a DefaultMetadata para enviarlas en cada llamada, que es el lugar habitual para un token de autorización o una cabecera de trazado.

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

Plazos y cancelación

El tiempo de espera por llamada se envía como la cabecera estándar grpc-timeout, de modo que el servidor puede abandonar en el mismo momento que lo hace tu cliente. Una llamada de streaming en curso puede detenerse en cualquier momento con CancelCall, pasando el identificador de stream devuelto cuando se abrió el stream.

Llamadas unarias

Una llamada unaria envía una solicitud y obtiene una respuesta. Call se bloquea hasta que llega la respuesta y devuelve un TsgcGRPCResponse con el StatusCode, el StatusMessage, los bytes de Data en bruto (o DataString) y los 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 mantener la interfaz de usuario receptiva, usa CallAsync. Devuelve de inmediato y entrega la respuesta a través del 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;

Streaming de servidor

Con el streaming de servidor envías una solicitud y el servidor devuelve un stream de mensajes. Cada mensaje genera OnGRPCStreamMessage, y OnGRPCStreamEnd se dispara una vez con el estado 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;

Streaming de cliente

El streaming de cliente es la imagen reflejada: envías un stream de mensajes y el servidor responde una vez. Abre el stream, envía cada mensaje y luego ciérralo para leer la única respuesta.

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 bidireccional

El streaming bidireccional ejecuta un intercambio full-duplex sobre un único stream HTTP/2: ambos lados envían cuando quieren. Abre el stream, envía mensajes según sea necesario, gestiona los mensajes entrantes en OnGRPCStreamMessage y ciérralo al 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 estado y gestión de errores

Cada respuesta lleva un StatusCode tipado, desde grpcOK a través del conjunto estándar de gRPC (grpcNOT_FOUND, grpcUNAVAILABLE, grpcDEADLINE_EXCEEDED, grpcUNAUTHENTICATED y el resto). Para las llamadas asíncronas y de streaming, OnGRPCError informa de un estado gRPC distinto de OK y OnGRPCException informa de un fallo de transporte o de conexión.

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;

Reintento automático

El cliente puede reintentar por ti las llamadas fallidas. Habilita RetryPolicy, establece el número de intentos y el backoff, y enumera los códigos de estado que deben desencadenar un reintento. Esta es la configuración típica para fallos transitorios 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 el registro o la renovación de un token, añade una entrada a la cadena Interceptors, que envuelve cada llamada en un solo lugar.

Qué hacer a continuación

Sobre este cliente genérico, sgcWebSockets incluye clientes tipados para los servicios gRPC de Google Cloud (Pub/Sub, Speech-to-Text, Translation, Vision, Natural Language, Cloud Storage, BigQuery y Vertex AI), de modo que llamas a métodos de alto nivel en lugar de ensamblar protobufs a mano. Hay un ejemplo listo para ejecutar en Demos\21.GRPC\01.GRPC_Client, y la referencia completa está en la página del producto gRPC Client.

¿Preguntas o ayuda para empezar? Ponte en contacto. Recibirás una respuesta de las personas que escribieron el código.