gRPC vs REST : Premiers pas avec le meilleur protocole d'API

Publié: 2022-07-22

Dans le paysage technologique actuel, la plupart des projets nécessitent l'utilisation d'API. Les API relient la communication entre des services qui peuvent représenter un système unique et complexe, mais peuvent également résider sur des machines distinctes ou utiliser plusieurs réseaux ou langages incompatibles.

De nombreuses technologies standard répondent aux besoins de communication interservices des systèmes distribués, tels que REST, SOAP, GraphQL ou gRPC. Alors que REST est une approche privilégiée, gRPC est un concurrent digne, offrant des performances élevées, des contrats typés et un excellent outillage.

Présentation de REST

Le transfert d'état représentatif (REST) ​​est un moyen de récupérer ou de manipuler les données d'un service. Une API REST est généralement construite sur le protocole HTTP, utilisant un URI pour sélectionner une ressource et un verbe HTTP (par exemple, GET, PUT, POST) pour sélectionner l'opération souhaitée. Les corps de requête et de réponse contiennent des données spécifiques à l'opération, tandis que leurs en-têtes fournissent des métadonnées. Pour illustrer, regardons un exemple simplifié de récupération d'un produit via une API REST.

Ici, nous demandons une ressource de produit avec un ID de 11 et demandons à l'API de répondre au format JSON :

 GET /products/11 HTTP/1.1 Accept: application/json

Compte tenu de cette requête, notre réponse (en-têtes non pertinents omis) peut ressembler à :

 HTTP/1.1 200 OK Content-Type: application/json { id: 11, name: "Purple Bowtie", sku: "purbow", price: { amount: 100, currencyCode: "USD" } }

Bien que JSON puisse être lisible par l'homme, il n'est pas optimal lorsqu'il est utilisé entre les services. La nature répétitive du référencement des noms de propriété, même lorsqu'ils sont compressés, peut entraîner des messages gonflés. Regardons une alternative pour répondre à cette préoccupation.

Présentation de gRPC

gRPC Remote Procedure Call (gRPC) est un protocole de communication open source, basé sur des contrats et multiplateforme qui simplifie et gère la communication interservices en exposant un ensemble de fonctions à des clients externes.

Construit sur HTTP/2, gRPC exploite des fonctionnalités telles que le streaming bidirectionnel et le Transport Layer Security (TLS) intégré. gRPC permet une communication plus efficace grâce à des charges utiles binaires sérialisées. Il utilise des tampons de protocole par défaut comme mécanisme de sérialisation des données structurées, similaire à l'utilisation de JSON par REST.

Contrairement à JSON, cependant, les tampons de protocole sont plus qu'un format sérialisé. Ils comprennent trois autres grandes parties :

  • Un langage de définition de contrat trouvé dans les fichiers .proto (Nous suivrons proto3, la dernière spécification de langage de tampon de protocole.)
  • Code de fonction accesseur généré
  • Bibliothèques d'exécution spécifiques au langage

Les fonctions distantes disponibles sur un service (définies dans un fichier .proto ) sont répertoriées à l'intérieur du nœud de service dans le fichier de tampon de protocole. En tant que développeurs, nous arrivons à définir ces fonctions et leurs paramètres à l'aide du système de type riche des tampons de protocole. Ce système prend en charge divers types numériques et de date, listes, dictionnaires et nullables pour définir nos messages d'entrée et de sortie.

Ces définitions de service doivent être disponibles à la fois pour le serveur et pour le client. Malheureusement, il n'y a pas de mécanisme par défaut pour partager ces définitions à part fournir un accès direct au fichier .proto lui-même.

Cet exemple de fichier .proto définit une fonction pour renvoyer une entrée de produit, à partir d'un ID :

 syntax = "proto3"; package product; service ProductCatalog { rpc GetProductDetails (ProductDetailsRequest) returns (ProductDetailsReply); } message ProductDetailsRequest { int32 id = 1; } message ProductDetailsReply { int32 id = 1; string name = 2; string sku = 3; Price price = 4; } message Price { float amount = 1; string currencyCode = 2; }
Extrait 1 : Définition du service ProductCatalog

Le typage strict et l'ordre des champs de proto3 rendent la désérialisation des messages considérablement moins contraignante que l'analyse de JSON.

Comparaison entre REST et gRPC

Pour récapituler, les points les plus significatifs lors de la comparaison entre REST et gRPC sont :

LE REPOS gRPC
Multiplateforme Oui Oui
Format des messages Personnalisé mais généralement JSON ou XML Tampons de protocole
Taille de la charge utile des messages Moyen large Petit
Complexité de traitement Supérieur (analyse de texte) Inférieur (structure binaire bien définie)
Prise en charge du navigateur Oui (natif) Oui (via gRPC-Web)

Là où des contrats moins stricts et des ajouts fréquents à la charge utile sont attendus, JSON et REST conviennent parfaitement. Lorsque les contrats ont tendance à rester plus statiques et que la rapidité est de la plus haute importance, le gRPC l'emporte généralement. Dans la plupart des projets sur lesquels j'ai travaillé, gRPC s'est avéré plus léger et plus performant que REST.

Mise en œuvre du service gRPC

Construisons un projet rationalisé pour explorer à quel point il est simple d'adopter gRPC.

Création du projet API

Pour commencer, nous allons créer un projet .NET 6 dans Visual Studio 2022 Community Edition (VS). Nous allons sélectionner le modèle de service gRPC ASP.NET Core et nommer à la fois le projet (nous utiliserons InventoryAPI ) et notre première solution ( Inventory ) .

Une boîte de dialogue "Configurer votre nouveau projet" dans Visual Studio 2022. Dans cet écran, nous avons tapé "InventoryAPI" dans le champ Nom du projet, nous avons sélectionné "C:\MyInventoryService" dans le champ Emplacement et tapé "Inventaire" dans le champ Nom de la solution . Nous avons laissé "Placer la solution et le projet dans le même répertoire" décoché.

Maintenant, choisissons le . Option NET 6.0 (support à long terme) pour notre framework :

Une boîte de dialogue d'informations supplémentaires dans Visual Studio 2022. Dans cet écran, nous avons sélectionné ".NET 6.0 (prise en charge à long terme)" dans la liste déroulante Framework. Nous avons laissé "Activer Docker" décoché.

Définir notre service produit

Maintenant que nous avons créé le projet, VS affiche un exemple de service de définition de prototype gRPC nommé Greeter . Nous réutiliserons les fichiers de base de Greeter pour répondre à nos besoins.

  • Pour créer notre contrat, nous allons remplacer le contenu de greet.proto par Snippet 1, en renommant le fichier product.proto .
  • Pour créer notre service, nous allons remplacer le contenu du fichier GreeterService.cs par Snippet 2, en renommant le fichier ProductCatalogService.cs .
 using Grpc.Core; using Product; namespace InventoryAPI.Services { public class ProductCatalogService : ProductCatalog.ProductCatalogBase { public override Task<ProductDetailsReply> GetProductDetails( ProductDetailsRequest request, ServerCallContext context) { return Task.FromResult(new ProductDetailsReply { Id = request.Id, Name = "Purple Bowtie", Sku = "purbow", Price = new Price { Amount = 100, CurrencyCode = "USD" } }); } } }
Extrait 2 : ProductCatalogService

Le service renvoie désormais un produit codé en dur. Pour que le service fonctionne, il suffit de modifier l'enregistrement du service dans Program.cs pour référencer le nouveau nom du service. Dans notre cas, nous allons renommer app.MapGrpcService<GreeterService>(); à app.MapGrpcService<ProductCatalogService>(); pour rendre notre nouvelle API exécutable.

Avertissement juste : ce n'est pas votre test de protocole standard

Bien que nous puissions être tentés de l'essayer, nous ne pouvons pas tester notre service gRPC via un navigateur visant son point de terminaison. Si nous devions tenter cela, nous recevrons un message d'erreur indiquant que la communication avec les points de terminaison gRPC doit être établie via un client gRPC.

Création du Client

Pour tester notre service, utilisons le modèle d'application console de base de VS et créons un client gRPC pour appeler l'API. J'ai nommé le mien InventoryApp .

Par commodité, référençons un chemin de fichier relatif par lequel nous partagerons notre contrat. Nous ajouterons la référence manuellement au fichier .csproj . Ensuite, nous mettrons à jour le chemin et définirons le mode Client . Remarque : Je vous recommande de vous familiariser avec la structure de vos dossiers locaux et d'avoir confiance en eux avant d'utiliser le référencement relatif.

Voici les références .proto , telles qu'elles apparaissent dans les fichiers de projet du service et du client :

Dossier de projet de service
(Code à copier dans le dossier du projet client)
Dossier de projet client
(Après collage et édition)
 <ItemGroup> <Content Update="Protos\product.proto" GrpcServices="Server" /> </ItemGroup>
 <ItemGroup> <Protobuf Include="..\InventoryAPI\Protos\product.proto" GrpcServices="Client" /> </ItemGroup>

Maintenant, pour appeler notre service, nous allons remplacer le contenu de Program.cs . Notre code accomplira un certain nombre d'objectifs :

  1. Créez un canal qui représente l'emplacement du point de terminaison de service (le port peut varier, consultez donc le fichier launchsettings.json pour la valeur réelle).
  2. Créez l'objet client.
  3. Construisez une requête simple.
  4. Envoyez la demande.
 using System.Text.Json; using Grpc.Net.Client; using Product; var channel = GrpcChannel.ForAddress("https://localhost:7200"); var client = new ProductCatalog.ProductCatalogClient(channel); var request = new ProductDetailsRequest { Id = 1 }; var response = await client.GetProductDetailsAsync(request); Console.WriteLine(JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = true })); Console.ReadKey();
Extrait 3 : Nouveau Program.cs

Préparation du lancement

Pour tester notre code, dans VS, nous ferons un clic droit sur la solution et choisirons Set Startup Projects . Dans la boîte de dialogue Pages de propriétés de la solution, nous allons :

  • Sélectionnez le bouton radio en regard de Plusieurs projets de démarrage et, dans le menu déroulant Action, définissez les deux projets ( InventoryAPI et InventoryApp ) sur Start .
  • Cliquez sur OK .

Nous pouvons maintenant démarrer la solution en cliquant sur Démarrer dans la barre d'outils VS (ou en appuyant sur la touche F5 ). Deux nouvelles fenêtres de console s'afficheront : une pour nous dire que le service est à l'écoute, l'autre pour nous montrer les détails du produit récupéré.

Partage de contrat gRPC

Utilisons maintenant une autre méthode pour connecter le client gRPC à la définition de notre service. La solution de partage de contrat la plus accessible au client consiste à rendre nos définitions disponibles via une URL. D'autres options sont soit très fragiles (fichier partagé via un chemin), soit nécessitent plus d'efforts (contrat partagé via un package natif). Le partage via une URL (comme le font SOAP et Swagger/OpenAPI) est flexible et nécessite moins de code.

Pour commencer, rendez le fichier .proto disponible en tant que contenu statique. Nous mettrons à jour notre code manuellement car l'interface utilisateur de l'action de génération est définie sur "Protobuf Compiler". Cette modification demande au compilateur de copier le fichier .proto afin qu'il puisse être servi à partir d'une adresse Web. Si ce paramètre était modifié via l'interface utilisateur VS, la construction se briserait. Notre première étape consiste donc à ajouter l'extrait 4 au fichier InventoryAPI.csproj :

 <ItemGroup> <Content Update="Protos\product.proto"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup> <ItemGroup> <Content Include="Protos\product.proto" CopyToPublishDirectory="PreserveNewest" /> </ItemGroup>
Extrait 4 : code à ajouter au fichier de projet de service InventoryAPI

Ensuite, nous insérons le code dans Snippet 5 en haut du fichier ProductCatalogService.cs pour configurer un point de terminaison pour renvoyer notre fichier .proto :

 using System.Net.Mime; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.FileProviders;
Extrait 5 : Importations d'espaces de Namespace

Et maintenant, nous ajoutons Snippet 6 juste avant app.Run() , également dans le fichier ProductCatalogService.cs :

 var provider = new FileExtensionContentTypeProvider(); provider.Mappings.Clear(); provider.Mappings[".proto"] = MediaTypeNames.Text.Plain; app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(Path.Combine(app.Environment.ContentRootPath, "Protos")), RequestPath = "/proto", ContentTypeProvider = provider }); app.UseRouting();
Extrait 6 : Code pour rendre les fichiers .proto accessibles via l'API

Avec les extraits 4 à 6 ajoutés, le contenu du fichier .proto doit être visible dans le navigateur.

Un nouveau client test

Nous voulons maintenant créer un nouveau client de console que nous connecterons à notre serveur existant avec l'assistant de dépendance de VS. Le problème est que cet assistant ne parle pas HTTP/2. Par conséquent, nous devons ajuster notre serveur pour parler via HTTP/1 et démarrer le serveur. Notre serveur mettant désormais à disposition son fichier .proto , nous pouvons créer un nouveau client de test qui se connecte à notre serveur via l'assistant gRPC.

  1. Pour changer notre serveur pour parler via HTTP/1, nous allons modifier notre fichier JSON appsettings.json :
    1. Ajustez le champ Protocol (qui se trouve dans le chemin Kestrel.EndpointDefaults.Protocols ) pour lire Https .
    2. Enregistrez le fichier.
  2. Pour que notre nouveau client puisse lire ces informations proto , le serveur doit être en cours d'exécution. À l'origine, nous avons démarré à la fois le client précédent et notre serveur à partir de la boîte de dialogue Définir les projets de démarrage de VS. Ajustez la solution de serveur pour démarrer uniquement le projet de serveur, puis démarrez la solution. (Maintenant que nous avons modifié la version HTTP, notre ancien client ne peut plus communiquer avec le serveur.)
  3. Ensuite, créez le nouveau client de test. Lancez une autre instance de VS. Nous allons répéter les étapes décrites dans la section Création du projet API , mais cette fois, nous choisirons le modèle d' application console . Nous nommerons notre projet et notre solution InventoryAppConnected .
  4. Une fois le châssis client créé, nous allons nous connecter à notre serveur gRPC. Développez le nouveau projet dans l'explorateur de solutions VS.
    1. Cliquez avec le bouton droit sur Dépendances et, dans le menu contextuel, sélectionnez Gérer les services connectés .
    2. Dans l'onglet Services connectés, cliquez sur Ajouter une référence de service et choisissez gRPC .
    3. Dans la boîte de dialogue Ajouter une référence de service, choisissez l'option URL et saisissez la version http de l'adresse du service (n'oubliez pas de saisir le numéro de port généré de manière aléatoire à partir de launchsettings.json ) .
    4. Cliquez sur Terminer pour ajouter une référence de service pouvant être facilement gérée.

N'hésitez pas à vérifier votre travail par rapport à l'exemple de code de cet exemple. Étant donné que, sous le capot, VS a généré le même client que nous avons utilisé lors de notre première série de tests, nous pouvons réutiliser textuellement le contenu du fichier Program.cs du service précédent.

Lorsque nous modifions un contrat, nous devons modifier la définition gRPC de notre client pour qu'elle corresponde à la définition .proto mise à jour. Pour ce faire, il nous suffit d'accéder aux services connectés de VS et d'actualiser l'entrée de service correspondante. Maintenant, notre projet gRPC est terminé et il est facile de synchroniser notre service et notre client.

Votre prochain projet candidat : gRPC

Notre implémentation de gRPC offre un aperçu de première main des avantages de l'utilisation de gRPC. REST et gRPC ont chacun leurs propres cas d'utilisation idéaux en fonction du type de contrat. Cependant, lorsque les deux options conviennent, je vous encourage à essayer gRPC, cela vous donnera une longueur d'avance dans l'avenir des API.