Comment utiliser le client gRPC dans Delphi

· Composants

Le composant TsgcGRPCClient apporte gRPC à Delphi et C++Builder sans aucun runtime externe. Il fait partie de l'édition sgcWebSockets Enterprise et fonctionne sur Windows, macOS, Linux, iOS et Android. Ce guide explique comment le client fonctionne, comment le configurer et comment effectuer chacun des quatre types d'appels gRPC depuis Delphi.

Comment ça fonctionne

gRPC, ce sont des messages Protocol Buffers encadrés sur HTTP/2. Chaque message est envoyé sous forme de charge utile préfixée par sa longueur sur un flux HTTP/2, la requête transporte des en-têtes tels que content-type: application/grpc et grpc-timeout, et le statut final arrive sous forme des trailers grpc-status et grpc-message.

sgcWebSockets embarque déjà une pile HTTP/2 complète, donc TsgcGRPCClient repose sur un transport TsgcHTTP2Client. Vous lui fournissez les octets de votre requête sérialisée, il se charge de l'encadrement, des en-têtes et du timeout, ouvre le flux HTTP/2, et analyse la réponse et les trailers pour produire une TsgcGRPCResponse typée. Comme le client travaille avec des TBytes bruts, vous pouvez l'associer à n'importe quelle bibliothèque Protocol Buffers que vous utilisez déjà.

Mise en place du client

Un canal gRPC est une connexion HTTP/2. Créez un TsgcHTTP2Client, dirigez-le vers l'hôte et le port, puis affectez-le à la propriété Client du composant gRPC. Pour TLS, le client HTTP/2 utilise OpenSSL ou Windows SChannel ; pour un serveur local en clair (h2c), réglez TLS sur 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;

Configuration du canal

Les paramètres applicables à l'ensemble du canal se trouvent dans ChannelOptions. Vous pouvez activer la compression gzip par message, choisir le type de contenu transmis, et augmenter les limites de taille des messages et des métadonnées.

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

Métadonnées et authentification

Les en-têtes personnalisés circulent en tant que métadonnées gRPC. Ajoutez-les à DefaultMetadata pour les envoyer à chaque appel, ce qui est l'endroit habituel pour un jeton d'autorisation ou un en-tête de traçage.

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

Délais et annulation

Un timeout par appel est envoyé sous forme de l'en-tête standard grpc-timeout, afin que le serveur puisse abandonner au même moment que votre client. Un appel en streaming en cours peut être arrêté à tout moment avec CancelCall, en passant l'identifiant du flux retourné lors de son ouverture.

Appels unaires

Un appel unaire envoie une requête et reçoit une réponse. Call bloque jusqu'à l'arrivée de la réponse et retourne une TsgcGRPCResponse avec le StatusCode, le StatusMessage, les octets bruts Data (ou DataString) et les 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;

Pour garder l'interface réactive, utilisez CallAsync. Elle retourne immédiatement et livre la réponse via l'événement 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 serveur

Avec le streaming serveur, vous envoyez une requête et le serveur retourne un flux de messages. Chaque message déclenche OnGRPCStreamMessage, et OnGRPCStreamEnd se déclenche une fois avec le statut 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 client

Le streaming client est l'image miroir : vous poussez un flux de messages et le serveur répond une seule fois. Ouvrez le flux, envoyez chaque message, puis fermez-le pour lire la réponse unique.

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 bidirectionnel

Le streaming bidirectionnel exécute un échange en duplex intégral sur un unique flux HTTP/2 : les deux côtés envoient quand ils le souhaitent. Ouvrez le flux, envoyez des messages selon vos besoins, traitez les messages entrants dans OnGRPCStreamMessage, et fermez une fois terminé.

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

Codes de statut et gestion des erreurs

Chaque réponse transporte un StatusCode typé, de grpcOK à l'ensemble standard gRPC (grpcNOT_FOUND, grpcUNAVAILABLE, grpcDEADLINE_EXCEEDED, grpcUNAUTHENTICATED et les autres). Pour les appels asynchrones et en streaming, OnGRPCError signale un statut gRPC non-OK et OnGRPCException signale une défaillance de transport ou de connexion.

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;

Nouvelle tentative automatique

Le client peut réessayer les appels échoués à votre place. Activez RetryPolicy, définissez le nombre de tentatives et le backoff, et listez les codes de statut qui doivent déclencher une nouvelle tentative. C'est la configuration typique pour les défaillances grpcUNAVAILABLE transitoires.

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

Pour une logique transversale telle que la journalisation ou le renouvellement d'un jeton, ajoutez une entrée à la chaîne Interceptors, qui enveloppe chaque appel en un seul endroit.

Pour aller plus loin

Au-dessus de ce client générique, sgcWebSockets fournit des clients typés pour les services gRPC de Google Cloud (Pub/Sub, Speech-to-Text, Translation, Vision, Natural Language, Cloud Storage, BigQuery et Vertex AI), de sorte que vous appelez des méthodes de haut niveau au lieu d'assembler des protobufs à la main. Un exemple prêt à l'emploi se trouve dans Demos\21.GRPC\01.GRPC_Client, et la référence complète est sur la page produit du client gRPC.

Des questions ou besoin d'aide pour démarrer ? Contactez-nous. Vous obtiendrez une réponse des personnes qui ont écrit le code.