Alors que nous avançons dans le développement des applications, parmi diverses choses, il y a une chose principale dont nous nous soucions moins : la puissance de calcul. Grâce à l’avènement des fournisseurs de cloud, nous sommes moins inquiets de gérer des centres de données. Tout est disponible en quelques secondes sur demande. Cela conduit également à une augmentation de la taille des données. De grandes données sont générées et transportées à l’aide de divers médias dans des requêtes uniques.
Avec l’augmentation de la taille des données, nous avons des activités comme la sérialisation, la désérialisation et les coûts de transport qui s’ajoutent. Bien que nous ne nous soucions pas des ressources de calcul, la latence devient un surcoût. Nous devons réduire le transport. De nombreux protocoles de messagerie ont été développés dans le passé pour résoudre ce problème. SOAP était encombrant, et REST est une version simplifiée, mais nous avons besoin d’un cadre encore plus efficace. C’est là que les Appels de Procédure Distante – APD – entrent en jeu.
Dans cet article, nous allons comprendre ce qu’est RPC et les différentes implémentations de RPC, en nous concentrant sur gRPC, qui est l’implémentation de RPC par Google. Nous comparerons également REST avec RPC et comprendrons divers aspects de gRPC, y compris la sécurité, les outils, et bien plus encore.
Qu’est-ce que RPC?
RPC signifie Appels de Procédure Distante. La définition est dans le nom lui-même. Les appels de procédure signifient simplement des appels de fonction/méthode ; c’est le mot « Distant » qui fait toute la différence. Et si nous pouvions faire un appel de fonction à distance?
En termes simples, si une fonction réside sur un serveur et doit être invoquée depuis le côté client, pourrions-nous la rendre aussi simple qu’un appel de méthode/fonction ? Essentiellement, ce que fait un RPC est de donner l’illusion au client qu’il invoque une méthode locale, mais en réalité, il invoque une méthode sur une machine distante qui abstrait les tâches de la couche réseau. La beauté de cela est que le contrat est maintenu très strict et transparent (nous en discuterons plus tard dans l’article).
Étapes impliquées dans un appel RPC:
Voici ce à quoi ressemble un processus typique de REST:
Les RPC réduisent le processus ci-dessous:
Cela est dû au fait que toutes les complications associées à la création d’une requête sont maintenant abstraites de nous (nous en discuterons dans la génération de code). Tout ce dont nous avons besoin de nous soucier est les données et la logique.
gRPC: Quoi, Pourquoi et Comment
Jusqu’à présent, nous avons discuté du RPC, qui signifie essentiellement faire des appels de fonction/méthode à distance — nous donnant ainsi des avantages comme « définition de contrat stricte, » « abstraire la transmission et la conversion des données, » « réduire la latence, » et ainsi de suite, que nous discuterons au fur et à mesure de ce post. Ce à quoi nous aimerions vraiment plonger en profondeur est l’une des implémentations du RPC. Le RPC est un concept et gRPC est un cadre basé sur celui-ci.
Il existe diverses implémentations de RPC. Elles sont:
-
gRPC (google)
-
Thrift (Facebook)
-
Finalge (Twitter)
La version RPC de Google est appelée gRPC. Elle a été introduite en 2015 et gagne en popularité depuis. C’est l’une des mécanismes de communication les plus choisis dans une architecture microservices.
gRPC utilise protocol buffers (un format de message open-source) comme méthode par défaut de communication entre le client et le serveur. De plus, gRPC utilise HTTP/2 comme protocole par défaut. Il existe quatre types de communication que gRPC prend en charge:
-
Unary [communication classique entre client et serveur]
Passons maintenant au format de message largement utilisé dans gRPC — les protocol buffers, alias protobufs. Un message protobuf ressemble à ceci :
message Person { |
Ici, ‘Person’ est le message que nous souhaitons transférer (dans le cadre d’une requête/réponse) qui possède les champs ‘name’ (type string), ‘id’ (type string) et ’email’ (type string). Les nombres 1, 2, 3 représentent la position des données (comme dans ‘name’, ‘id’, et ‘has_ponycopter’) lorsqu’elles sont sérialisées au format binaire.
Une fois que le développeur a créé le fichier(s) Protocol Buffer avec toutes les messages, nous pouvons utiliser un compilateur de protocole (un binaire) pour compiler le fichier Protocol Buffer écrit, ce qui génèrera toutes les classes d’utilité et les méthodes nécessaires pour travailler avec le message. Par exemple, comme indiqué ici, le code généré (en fonction de la langue choisie) ressemblera à cela.
Comment Définissons-nous les Services?
Nous devons définir des services qui utilisent les messages ci-dessus à envoyer/recevoir.
Après avoir écrit les types de messages de requête et de réponse nécessaires, la prochaine étape consiste à écrire le service lui-même.
Les services gRPC sont également définis dans les Protocol Buffers et ils utilisent les mots-clés « service » et « RPC » pour définir un service.
Jetez un œil au contenu du fichier proto ci-dessous:
message HelloRequest { message HelloResponse { service HelloService { |
Ici, HelloRequest et HelloResponse sont les messages et HelloService expose un RPC unaire appelé SayHello qui prend HelloRequest en entrée et donne HelloResponse en sortie.
Comme mentionné, HelloService contient actuellement un seul RPC unaire. Mais il pourrait en contenir plus d’un. De plus, il peut contenir une variété de RPC (unaire/streaming côté client/streaming côté serveur/Bidirectionnel).
Pour définir un RPC streaming, il suffit d’ajouter ‘stream ‘ avant l’argument de requête/réponse, Définitions proto des RPCs streaming, et code généré.
Dans le lien de code-base ci-dessus :
-
streaming.proto: ce fichier est défini par l’utilisateur
-
streaming.pb.go & streaming_grpc.pb.go: ces fichiers sont générés automatiquement lors de l’exécution de la commande proto-compiler.
gRPC Vs. REST
Nous avons beaucoup parlé de gRPC. Aussi, il y a eu une mention de REST. Ce que nous avons manqué, c’est de discuter de la différence. Je veux dire, quand nous avons un cadre de communication léger et bien établi sous la forme de REST, pourquoi chercher un autre cadre de communication? Commençons à comprendre davantage gRPC par rapport à REST, ainsi que les avantages et les inconvénients de chacun.
Pour comparer, ce dont nous avons besoin sont des paramètres. Alors décomposons la comparaison en fonction des paramètres suivants:
-
Format du message : protocol buffers vs JSON
-
La vitesse d’écriture et de lecture est bien meilleure dans le cas des protocol buffers quelle que soit la taille des données (petite/moyenne/grande). Résultats des tests de benchmark.
-
Après sérialisation, JSON est lisible par l’homme tandis que les protobufs (en format binaire) ne le sont pas. On ne sait pas si c’est un inconvénient ou non car parfois on aimerait voir les détails de la requête dans l’outil des développeurs Google ou les sujets Kafka et dans le cas des protobufs, on ne peut rien comprendre.
-
-
Protocole de communication : HTTP 1.1 vs. HTTP/2T
-
REST repose sur HTTP 1.1 ; la communication entre un client REST et un serveur nécessiterait une connexion TCP établie qui implique à son tour un échange de trois messages. Lorsque nous obtenons une réponse du serveur après avoir envoyé une requête depuis le client, la connexion TCP n’existe plus après cela. Une nouvelle connexion TCP doit être mise en place afin de traiter une autre requête. Cet établissement d’une connexion TCP à chaque requête augmente la latence.
-
Ainsi, gRPC, qui repose sur HTTP 2, a rencontré ce défi en ayant une connexion persistante. Il faut se rappeler que les connexions persistantes dans HTTP 2 sont différentes de celles des web sockets où une connexion TCP est détournée et la transmission de données est non surveillée. Dans une connexion gRPC, une fois qu’une connexion TCP est établie, elle est réutilisée pour plusieurs requêtes. Toutes les requêtes provenant du même couple client-serveur sont multiplexées sur la même connexion TCP.
-
-
Juste se soucier des données et de la logique : La génération de code étant une citoyenne de première classe
-
Les fonctionnalités de génération de code sont intégrées à gRPC via son compilateur protoc intégré. Avec les API REST, il est nécessaire d’utiliser un outil tiers comme Swagger pour générer automatiquement le code pour les appels d’API dans divers langages.
-
Dans le cas de gRPC, il simplifie le processus de marshallage/démarshallage, de configuration d’une connexion et d’envoi/réception de messages ; tout ce dont nous avons besoin de nous soucier, c’est des données que nous voulons envoyer ou recevoir et de la logique.
-
-
Vitesse de transmission
-
Étant donné que le format binaire est beaucoup plus léger que le format JSON, la vitesse de transmission dans le cas de gRPC est 7 à 10 fois plus rapide que celle de REST.
-
Fonctionnalité |
REST |
gRPC |
Protocole de communication |
Adopte le modèle requête-réponse. Il peut fonctionner avec n’importe quelle version HTTP mais est généralement utilisé avec HTTP 1.1 |
Adopte le modèle client-réponse et est basé sur HTTP 2. Certains serveurs ont des solutions pour le faire fonctionner avec HTTP 1.1 (via des passerelles REST) |
Prise en charge par les navigateurs |
Fonctionne partout |
Support limité. Il est nécessaire d’utiliser gRPC-Web, qui est une extension pour le web et repose sur HTTP 1.1 |
Structure de données de charge utile |
Utilise principalement des charges utiles basées sur JSON et XML pour transmettre des données |
Utilise par défaut des protocol buffers pour transmettre des charges utiles |
Génération de code |
Il est nécessaire d’utiliser des outils tiers comme Swagger pour générer du code client |
gRPC offre un support natif pour la génération de code pour diverses langues |
Mise en cache des requêtes |
Facile à mettre en cache des requêtes côté client et serveur. La plupart des clients/serveurs le prennent en charge nativement (par exemple via des cookies) |
Ne prend pas en charge par défaut le mise en cache des requêtes/réponses |
Encore une fois, pour le moment, gRPC ne dispose pas de support pour les navigateurs puisque la plupart des frameworks d’interface utilisateur ont encore un support limité ou inexistant pour gRPC. Bien que gRPC soit généralement la solution automatique dans la plupart des cas lorsqu’il s’agit de communication entre microservices internes, ce n’est pas le cas pour la communication externe qui nécessite une intégration d’interface utilisateur.
Maintenant que nous avons fait une comparaison des deux frameworks : gRPC et REST. Lequel utiliser et quand?
-
Dans une architecture de microservices avec plusieurs microservices légers, où l’efficacité de la transmission de données est primordiale, gRPC serait un choix idéal.
-
Si la génération de code avec support pour plusieurs langues est une exigence, gRPC devrait être le cadre de référence.
-
Avec les capacités de streaming de gRPC, les applications en temps réel comme le trading ou les services OTT en bénéficieraient plutôt que d’utiliser le polling avec REST.
-
Si le bandeau est un problème, gRPC offrirait une latence et un débit beaucoup plus faibles.
-
Si une mise en œuvre plus rapide et une itération à grande vitesse est une exigence, REST devrait être l’option de référence.
Concepts gRPC
Équilibrage de charge
Bien que la connexion persistante résolve le problème de latence, elle soulève un autre défi sous la forme d’équilibrage de charge. Puisque gRPC (ou HTTP2) crée des connexions persistantes, même en présence d’un équilibreur de charge, le client forme une connexion persistante avec le serveur qui se trouve derrière l’équilibreur de charge. Ceci est analogue à une session collante.
Nous pouvons comprendre le problème ou le défi à travers une démonstration. Et le code et les fichiers de déploiement sont disponibles à l’adresse : https://github.com/infracloudio/grpc-blog/tree/master/grpc-loadbalancing.
À partir de la base de code de démo ci-dessus, nous pouvons constater que la responsabilité du load balancing incombe au client. Cela conduit à la constatation que l’avantage de gRPC, à savoir la connexion persistante, n’existe pas avec ce changement. Mais gRPC peut toujours être utilisé pour ses autres avantages.
Lire plus sur le load balancing dans gRPC.
Dans la base de code de démo ci-dessus, seule une stratégie de load balancing par roue de loterie est utilisée/présentée. Mais gRPC prend également en charge une autre stratégie de load balancing basée sur le client appelée OOB « pick-first ».
De plus, le load balancing personnalisé côté client est également pris en charge.
Contrat Propre
Dans REST, le contrat entre le client et le serveur est documenté mais pas strict. Si nous remontons encore plus loin jusqu’à SOAP, les contrats étaient exposés via des fichiers wsdl. Dans REST, nous exposons les contrats via Swagger et d’autres dispositions. Mais la rigueur manque, nous ne pouvons pas savoir avec certitude si le contrat a changé du côté du serveur pendant que le code du client est en développement.
Avec gRPC, le contrat, que ce soit via des fichiers proto ou des stubs générés à partir de fichiers proto, est partagé entre le client et le serveur. Cela ressemble à appeler une fonction mais de manière distante. Et puisque nous faisons une appel de fonction, nous savons exactement ce que nous devons envoyer et ce que nous attendons en réponse. La complexité de créer des connexions avec le client, de prendre en charge la sécurité, la sérialisation-désérialisation, etc. sont abstraites. Tout ce qui nous importe est les données.
Considérez la base de code suivante:
https://github.com/infracloudio/grpc-blog/tree/master/greet_app
Le client utilise le stub (code généré à partir du fichier proto) pour créer un objet client et invoquer l’appel de fonction à distance:
```sh
import greetpb "github.com/infracloudio/grpc-blog/greet_app/internal/pkg/proto"
cc, err := grpc.Dial(“<server-address>”, opts)
if err != nil {
log.Fatalf("could not connect: %v", err)
}
c := greetpb.NewGreetServiceClient(cc)
res, err := c.Greet(context.Background(), req)
if err != nil {
log.Fatalf("error while calling greet rpc : %v", err)
}
```
De même, le serveur utilise également le même stub (code généré à partir du fichier proto) pour recevoir l’objet de requête et créer l’objet de réponse:
```sh
import greetpb "github.com/infracloudio/grpc-blog/greet_app/internal/pkg/proto"
func (*server) Greet(_ context.Context, req *greetpb.GreetingRequest) (*greetpb.GreetingResponse, error) {
// faire quelque chose avec 'req'
return &greetpb.GreetingResponse{
Result: result,
}, nil
}
```
Les deux utilisent le même stub généré à partir du fichier proto situé ici.
Et le stub a été généré en utilisant la commande de compilation de proto ci-dessous.
```sh
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative internal/pkg/proto/*.proto
```
Sécurité
L’authentification et l’autorisation gRPC fonctionnent sur deux niveaux :
-
L’authentification/autorisation au niveau de l’appel est généralement gérée à travers des jetons qui sont appliqués dans les métadonnées lors de l’appel. Exemple d’authentification basée sur des jetons.
-
L’authentification au niveau du canal utilise un certificat client qui est appliqué au niveau de la connexion. Il peut également inclure des informations d’authentification/autorisation au niveau de l’appel à appliquer automatiquement à chaque appel sur le canal. Exemple d’authentification basée sur un certificat.
L’un ou les deux de ces mécanismes peuvent être utilisés pour aider à sécuriser les services.
Middleware
Dans REST, nous utilisons le middleware à diverses fins, telles que:
-
Limitation de débit
-
Validation pré/post requête/réponse
-
Traitement des menaces de sécurité
Nous pouvons réaliser la même chose avec gRPC. La terminologie est différente dans gRPC — on les appelle des intercepteurs, mais ils effectuent des activités similaires.
Dans la branche middleware du code base ‘greet_app’, nous avons intégré des intercepteurs de journalisation et de Prometheus.
Voyez comment les intercepteurs sont configurés pour utiliser les packages Prometheus et de journalisation ici.
Mais nous pouvons intégrer d’autres packages aux intercepteurs pour des objectifs tels que la prévention des plantages et la récupération (pour gérer les exceptions), le tracing, voire l’authentification, et plus encore.
Middleware pris en charge par le framework gRPC.
Emballage, Numérotation et Pratiques de Codage des Fichiers Proto
Emballage
Suivons la branche de packaging.
Tout d’abord, commençons par ‘Taskfile.yaml’, la tâche ‘gen-pkg’ indique ‘protoc –proto_path=packaging packaging/*.proto –go_out=packaging’. Cela signifie que ‘protoc’ (le compilateur) convertira tous les fichiers dans ‘packaging/*.proto’ en leurs équivalents ‘go’, comme indiqué par l’indicateur ‘–go_out=packaging’, dans le répertoire ‘packaging’ lui-même.
Deuxièmement, dans le fichier ‘processor.proto’, 2 messages ont été définis, à savoir ‘CPU’ et ‘GPU’. Alors que CPU est un message simple avec 3 champs de types de données intégrés, GPU, en revanche, a un type de données personnalisé appelé ‘Memory’. ‘Memory’ est un message distinct et est défini dans un fichier différent.
Alors, comment utilisez-vous le message ‘Memory’ dans le fichier ‘processor.proto’? En utilisant import.
Même si vous essayez de générer un fichier proto en exécutant la tâche ‘gen-pkg’ après avoir mentionné l’importation, cela lancera une erreur. Car par défaut, ‘protoc’ suppose que les deux fichiers ‘memory.proto’ et ‘processor.proto’ sont dans des packages différents. Vous devez donc mentionner le même nom de package dans les deux fichiers.
L’optionnel ‘go_package’ indique au compilateur de créer un nom de package comme ‘pb’ pour les fichiers go. Si d’autres fichiers proto liés à un langage devaient être créés, le nom du package serait ‘laptop_pkg’.
Versioning
-
Il peut y avoir deux types de modifications dans gRPC : celles qui cassent et celles qui ne cassent pas.
-
Les modifications non cassantes incluent l’ajout d’un nouveau service, l’ajout d’une nouvelle méthode à un service, l’ajout d’un champ à la proto de requête ou de réponse, et l’ajout d’une valeur à un énuméré
-
Les modifications cassantes comme renommer un champ, changer le type de données d’un champ, le numéro de champ, renommer ou supprimer un paquet, un service ou des méthodes nécessitent la versionnage des services
-
Facultatif emballage.
Pratiques de codage
-
Le message de requête doit se terminer par la requête `CreateUserRequest`
-
Le message de réponse doit se terminer par la requête `CreateUserResponse`
-
Dans le cas où le message de réponse est vide, vous pouvez soit utiliser un objet vide `CreateUserResponse` soit utiliser `google.protobuf.Empty`
-
Le nom du paquet doit être significatif et doit être versionné, par exemple : paquet `com.ic.internal_api.service1.v1`
Outils
L’écosystème gRPC prend en charge un ensemble d’outils pour faciliter les tâches non liées au développement, comme la documentation, le gateway REST pour un serveur gRPC, l’intégration de validateurs personnalisés, le linting, etc. Voici quelques outils qui peuvent nous aider à atteindre le même objectif:
-
protoc-gen-grpc-gateway — plugin pour créer un gateway REST d’API gRPC. Il permet d’utiliser les points de terminaison gRPC comme points de terminaison REST et effectue la traduction de JSON vers proto. Fondamentalement, vous définissez un service gRPC avec certaines annotations personnalisées et il rend ces méthodes gRPC accessibles via REST à l’aide de requêtes JSON.
-
protoc-gen-swagger — un plugin complémentaire pour grpc-gateway. Il est capable de générer swagger.json en fonction des annotations personnalisées requises pour le gateway gRPC. Vous pouvez ensuite importer ce fichier dans votre client REST de choix (comme Postman) et effectuer des appels d’API REST aux méthodes que vous avez exposées.
-
protoc-gen-grpc-web — un plugin qui permet à notre interface frontale de communiquer avec le backend en utilisant des appels gRPC. Un billet de blog distinct sur ce sujet sera publié à l’avenir.
-
protoc-gen-go-validators — un plugin qui permet de définir des règles de validation pour les champs des messages proto. Il génère une méthode Validate() error pour les messages proto que vous pouvez appeler en GoLang pour vérifier si le message correspond à vos attentes prédéfinies.
-
https://github.com/yoheimuta/protolint — un plugin pour ajouter des règles de lint aux fichiers proto
Test avec POSTMAN
Contrairement aux tests d’API REST avec Postman ou des outils équivalents comme Insomnia, il n’est pas tout à fait confortable de tester des services gRPC.
Note : Les services gRPC peuvent également être testés depuis la ligne de commande à l’aide d’outils comme evans-cli. Mais pour cela, la réflexion doit être activée (si elle n’est pas activée, le chemin vers le fichier proto est requis) sur les serveurs gRPC. Modifications à apporter pour activer la réflexion et comment entrer dans le mode REPL de evans-cli. Après avoir entré en mode REPL de evans-cli, les services gRPC peuvent être testés directement depuis la ligne de commande et le processus est décrit sur la page GitHub d’evans-cli.
Postman dispose d’une version bêta pour tester les services gRPC.
Voici les étapes pour le faire :
-
Ouvrir Postman
-
Aller dans ‘APIs’ dans la barre latérale gauche
-
Cliquer sur le signe ‘+’ pour créer une nouvelle API :
-
Dans la fenêtre contextuelle, entrer ‘Nom’, ‘Version’, et ‘Détails du schéma’ et cliquer sur créer [sauf si vous devez importer à partir de sources comme GitHub/Bitbucket]. Cette étape est pertinente si vous souhaitez copier-coller le contrat proto.
5. Votre API est créée comme indiqué ci-dessous. Cliquez sur la version ‘1.0.0’, allez dans la définition et entrez votre contrat proto.
-
N’oubliez pas que l’importation ne fonctionne pas ici, il serait donc préférable de conserver tous les protos dépendants au même endroit.
-
Les étapes ci-dessus aideront à conserver les contrats pour une utilisation future.
-
Ensuite, cliquez sur ‘Nouveau’ et sélectionnez ‘Requête gRPC’ :
-
Entrez l’URI et choisissez le proto dans la liste des API enregistrées :
-
Entrez votre message de requête et ‘Invoquer’ :
Dans les étapes ci-dessus, nous avons déterminé le processus pour tester nos API gRPC via POSTMAN. Le processus de test des points de terminaison gRPC diffère de celui des points de terminaison REST utilisant POSTMAN. Une chose à retenir est que lors de la création et de l’enregistrement du contrat proto comme en #5, tous les définitions de message et de service proto doivent être au même endroit. Comme il n’y a aucune possibilité d’accéder aux messages proto entre les versions dans POSTMAN.
Conclusion
Dans cette publication, nous avons développé une idée sur RPC, établi des parallèles avec REST ainsi que discuté de leurs différences, puis nous avons abordé une implémentation de RPC, à savoir gRPC développé par Google.
gRPC en tant que framework peut être crucial, surtout pour l’architecture basée sur des microservices pour la communication interne. Il peut être utilisé pour la communication externe également mais nécessitera un gateway REST. gRPC est indispensable pour les applications de streaming et en temps réel.
La manière dont Golang se prouve comme une langue de script côté serveur, gRPC se prouve comme un framework de communication de facto.
Source:
https://dzone.com/articles/understanding-grpc-concepts-use-cases-amp-best-pra