Article
· Juil 27, 2022 19m de lecture

gRPC - de quoi s'agit-il et le Hello World

Introduction

Cet article a pour but de donner une introduction à ce qu'est gRPC ainsi que de donner un exemple de comment jouer avec le Hello World officiel en utilisant IRIS Embedded Python.

Vous pouvez trouver tout le code exposé ici, dans le référentiel suivant project repo.

gRPC

Le gRPC ( appel de procédure à distance gRPC) est un style architectural d'API basé sur le protocole RPC. Le projet a été créé par Google en 2015 et est sous licence Apache 2.0. Actuellement, le projet est soutenu par la fondation Cloud Native Computing Foundation (CNCF).

Les cas d'utilisation réussis sont liés à la connexion de services entre backends, tels que les services dans une architecture de type microservices.

Protocol buffer

La plupart des protocoles basés sur le RPC utilisent un IDL (langage de description d'interface) pour définir un contrat de communication entre un serveur et un client.

Le gRPC utilise un format de mécanisme de sérialisation appelé "Protocol Buffer".

L'objectif d'un tel format est similaire à celui d'un WSDL, dans lequel vous pouvez définir des méthodes et des structures de données. Toutefois, contrairement au WSDL, qui est défini à l'aide de XML, le Protocol Buffer utilise un langage (le langage du Protocol Buffer) semblable à un mélange des langages les plus courants.

Par exemple, pour définir un message d'informations d'interopérabilité, vous pouvez utiliser la définition de tampon de protocole suivante :

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;
}

Vous pouvez également définir des contrats de méthodes de service pour les messages. Par exemple :

//La définition du service d'accueil.
service Greeter {
  // Envoi d'un message d'accueil
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// Le message de requête contenant le nom de l'utilisateur.
message HelloRequest {
  string name = 1;
}

// Le message de réponse contenant les messages d'accueil
message HelloReply {
  string message = 1;
}

L'utilisation de Protocol Buffer dans gRPC lui permet de suivre un principe de conception fonctionnel plutôt que basé sur les ressources, utilisé par REST.

Outils gRPC

Ce que vous définissez dans le langage des Protocoles Buffer est inutilisable directement. Vous devez transpiler le langage des tampons de protocole vers un autre langage, qui est supporté par gRPC.

Ce transpilage est effectué par un paquetage appelé outils gRPC. Actuellement, la plateforme gRPC supporte des langages tels que Java, C++, Dart, Python, Objective-C, C#, Ruby, JavaScript et Go.

Dans cet article, nous allons utiliser le support Python afin d'utiliser gRPC avec la fonctionnalité Embedded Python dans IRIS.

Par exemple, avec cette commande des outils gRPC, vous pouvez transpiler la définition des tampons de protocole pour le service Greeter, les messages HelloRequest et HelloReply vers Python :

python3 -m grpc_tools.protoc -I ../../protos --python_out=. --grpc_python_out=. ../../protos/helloworld.proto

Cette commande produit ces fichiers Python :

-rwxrwxrwx  1 irisowner irisowner 2161 Mar 13 20:01 helloworld_pb2.py*
-rwxrwxrwx  1 irisowner irisowner 3802 Mar 13 20:01 helloworld_pb2_grpc.py*

Ces fichiers sont le code source Python généré à partir du fichier proto pour les messages et les méthodes de service, respectivement. Le serveur met en œuvre les méthodes de service, et le client (également appelé stub) les appelle.

Ainsi, vous pouvez utiliser Embedded Python pour envoyer/recevoir des messages Hello via le service Greeter.

Un autre outil utile pour gRPC est un équivalent de curl, l'utilitaire grpcurl. Après avoir introduit notre exemple "hello world", un aperçu de la façon d'utiliser un tel outil sera présenté.

Types de méthodes de service

Les clients peuvent varier en fonction de la manière dont ils envoient et reçoivent les messages des méthodes de service. Les messages peuvent être envoyés et reçus un par appel ou dans un flux. Pour chaque combinaison, gRPC dispose d'un type de méthode de service :

  • RPC simple ou unaire : les clients envoient un message simple et reçoivent une réponse simple du serveur, c'est-à-dire un appel de fonction standard ;
  • Response-streaming ou server streaming : les clients envoient un message simple et reçoivent un flux de messages du serveur ;
  • Request-streaming ou streaming client : les clients envoient un flux de messages et reçoivent un message simple du serveur ;
  • Bidirectional-streaming : les clients envoient un flux de messages et reçoivent un autre flux de messages du serveur.

La communication par ces méthodes peut être asynchrone (par défaut) ou synchrone, selon les besoins du client.

TLa page gRPC core-concepts définit ces types comme des caractéristiques du cycle de vie de gRPC. Elle met également en évidence d'autres fonctionnalités qui n'entrent pas dans le cadre de cette introduction, mais vous pouvez consulter les liens ci-dessous si vous souhaitez obtenir de plus amples informations :
* Délais/Temps mort
* Fin du RPC
* Annulation de RPC
* Métadonnées
* Canaux

Avantages et inconvénients

Voici quelques avantages et inconvénients que j'ai trouvés dans certains articles :

Avantages :

  • Des messages plus légers. Le tampon du protocole est un format binaire, ce qui permet d'éviter la surcharge JSON créée par les caractères spéciaux.
  • Sérialisation/désérialisation rapide. Encore une fois, en raison de son format binaire, les tampons de protocole pourraient être sérialisés/désérialisés dans des stubs clients utilisant des langages spécifiques sans interpréteurs.
  • Clients intégrés (stubs). Les tampons de protocole ont des générateurs intégrés pour la plupart des langages utilisés, contrairement à JSON qui dépend d'outils tiers comme OpenAPI et ses générateurs de clients.
  • Requêtes parallèles. HTTP/1 autorise jusqu'à 6 connexions simultanées, bloquant toute autre demande jusqu'à ce que les 6 connexions soient terminées - un problème connu sous le nom de HOL (head of line blocking) ; HTTP/2 corrige ces limitations.
  • Conception d'API axée sur le contrat. Bien que les API REST puissent exposer un contrat par le biais d'outils tiers comme OpenAPI, dans gRPC, un tel contrat est explicitement déclaré par les tampons du protocole.
  • Streaming natif. Grâce aux capacités de streaming de HTTP/2, gRPC permet un modèle de streaming bidirectionnel natif, contrairement à REST, qui doit imiter ce comportement sur HTTP/1.

Inconvénients :

  • Des messages plus légers. Le tampon du protocole est un format binaire, ce qui permet d'éviter la surcharge JSON créée par les caractères spéciaux.
  • Sérialisation/désérialisation rapide. Encore une fois, en raison de son format binaire, les tampons de protocole pourraient être sérialisés/désérialisés dans des stubs clients utilisant des langages spécifiques sans interpréteurs.
  • Clients intégrés (stubs). Les tampons de protocole ont des générateurs intégrés pour la plupart des langages utilisés, contrairement à JSON qui dépend d'outils tiers comme OpenAPI et ses générateurs de clients.
  • Requêtes parallèles. HTTP/1 autorise jusqu'à 6 connexions simultanées, bloquant toute autre demande jusqu'à ce que les 6 connexions soient terminées - un problème connu sous le nom de HOL (head of line blocking) ; HTTP/2 corrige ces limitations.
  • Conception d'API axée sur le contrat. Bien que les API REST puissent exposer un contrat par le biais d'outils tiers comme OpenAPI, dans gRPC, un tel contrat est explicitement déclaré par les tampons du protocole.
  • Streaming natif. Grâce aux capacités de streaming de HTTP/2, gRPC permet un modèle de streaming bidirectionnel natif, contrairement à REST, qui doit imiter ce comportement sur HTTP/1.

Toutefois, ces avantages et inconvénients ne constituent pas un consensus. Ils dépendent fortement des besoins de votre application.

Par exemple, un inconvénient supposé de REST/JSON est le besoin d'outils tiers comme OpenAPI. Cependant, ce n'est pas forcément un problème puisque ces outils sont largement supportés, maintenus et utilisés par plusieurs communautés/entreprises de développement dans le monde.

D'autre part, si votre projet doit faire face à des complexités que gRPC peut résoudre mieux que REST, vous devriez choisir gRPC, même si cette décision entraîne quelques complications, comme la création d'une équipe de développeurs qualifiés.

Où et quand faut-il utiliser le gRPC ?

Voici quelques cas d'utilisation où vous avez besoin de gRPC :

  • Communication par microservices
  • Application client/serveur, où les clients fonctionnent sur un matériel et/ou un réseau limité, en supposant que HTTP/2 soit disponible.
  • Interopérabilité facilitée par la conception d'une solide API contractuelle

Les acteurs du gRPC

Certaines grandes entreprises utilisent gRPC pour relever des défis spécifiques :

  • Salesforce : gRPC permet à la plate-forme de l'entreprise d'accroître la robustesse de l'interopérabilité grâce à la conception de contrats solides fournis par les tampons de protocole.
  • Netflix : utilise gRPC pour améliorer son environnement de microservices.
  • Spotify : comme Netflix, utilise gRPC pour relever les défis des microservices et gérer de nombreuses API.

Hello world via Embedded Python

Ok, après une brève introduction sur ce que représente gRPC et ce qu'il fait, vous pouvez maintenant le gérer, alors jouons un peu. Comme le dit le titre de cet article, il s'agit d'une adaptation de l'exemple original de hello world utilisant IRIS Embedded Python.

En fait, cet exemple est une modification de deux autres instances que l'on peut trouver dans le dépôt d'échantillons gRPC - helloworld et hellostreamingworld. À l'aide de cet exemple, je voudrais vous montrer comment envoyer et recevoir un message simple en mode simple et en mode flux. Bien qu'il s'agisse d'un exemple simple sans fonctionnalités vraiment utiles, il vous aidera à comprendre les principaux concepts liés au développement d'une application gRPC.

Installation de gRPC pour Python

Tout d'abord, installons les paquets nécessaires à l'utilisation de gRPC en Python. Si vous exécutez l'exemple à partir du conteneur défini dans mon projet github project, et si vous avez probablement déjà installé ces paquets, vous pouvez donc sauter cette étape.

python3 -m pip install --upgrade pip
python3 -m pip install --upgrade --target /usr/irissys/mgr/python grpcio grpcio-tools

Définir le contrat de service

Voyons maintenant le contrat de service, c'est-à-dire le fichier tampon du protocole (ou simplement protobuf pour faire court), avec le schéma des messages et les méthodes disponibles :

syntax = "proto3";

package helloworld;

// La définition du service d'accueil.
service MultiGreeter {
  // Envoi d'un message d'accueil
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  // Envoi de messages d'accueil multiples
  rpc SayHelloStream (HelloRequest) returns (stream HelloReply) {}
}

// Le message de requête contenant le nom de l'utilisateur et le nombre de messages d'accueil.
// qu'il souhaite.
message HelloRequest {
  string name = 1;
  Int32 num_greetings = 2;
}

// Un message de réponse contenant un message d'accueil
message HelloReply {
  string message = 1;
}

Ce fichier protobuf définit un service appelé MultiGreeter avec deux méthodes RPC, SayHello() et SayHelloStream().

La méthode SayHello() reçoit un message HelloRequest et envoie un message HelloReply. De même, la méthode SayHelloStream() reçoit et envoie les mêmes messages, mais elle envoie un flux de messages HelloRequest au lieu d'un message unique.

Après la définition du service, il y a les définitions des messages, HelloRequest et HelloReply. Le message HelloRequest encapsule juste deux champs : une chaîne de caractères appelée name et un entier appelé num_greetings. Le message HelloReply ne contient qu'un seul champ, une chaîne de caractères appelée message.

Les chiffres qui suivent les champs sont appelés numéros de champ. Ces numéros ne doivent pas être modifiés une fois que le message est utilisé, car ils servent d'identifiants.

Génération de code Python à partir du contrat de service

Comme vous pouvez probablement le remarquer, nous n'avons pas besoin d'écrire de code dans la définition de protobuf, seulement des interfaces. La tâche de mettre en œuvre le code pour différents langages de programmation est faite par le compilateur protoc de protobuf. Il existe un compilateur protoc pour chacun des langages supportés par gRPC.

Pour Python, le compilateur est déployé comme un module appelé grpc_tools.protoc.

Pour compiler la définition du protobuf en code Python, exécutez la commande suivante (en supposant que vous utilisez mon projet):

cd /irisrun/repo/jrpereira/python/grpc-test
/usr/irissys/bin/irispython -m grpc_tools.protoc -I ./ --python_out ./ --grpc_python_out ./ helloworld.proto

Cette commande invoque le module grpc_tools.protoc - le compilateur protoc pour Python, avec les paramètres suivants :

  • helloword.proto : le fichier .proto principal pour le contrat de service
  • -I : l'emplacement des fichiers .proto où le compilateur cherchera les dépendances
  • --python_out : l'emplacement du code Python généré pour les messages
  • --grpc_python_out : l'emplacement du code Python généré pour un serveur et un stub (client) basé sur les méthodes RPC dans la définition du service

Dans ce cas, tous ces paramètres de localisation sont configurés pour le répertoire courant.

Le code généré par le compilateur protoc n'est pas le meilleur exemple de lisibilité, bien qu'il ne soit pas si difficile à comprendre. Vous pouvez le vérifier dans le répertoire passé au compilateur protoc.

De toute façon, ces fichiers sont destinés à être importés dans votre propre code, alors utilisons-les, en mettant en œuvre un serveur et un client.

Mise en œuvre d'un serveur pour le service

Afin de mettre en œuvre un serveur pour le service défini ci-dessus, utilisons Embedded Python.

Tout d'abord, définissons un serveur en utilisant un fichier Python où la logique du serveur est mise en œuvre. J'ai décidé de le mettre en œuvre de cette façon en raison de la nécessité d'utiliser une bibliothèque de concurrence Python.

"""
La mise en œuvre de Python pour le serveur GRPC helloworld.Greeter.
Adapté de:
    - https://github.com/grpc/grpc/blob/master/examples/python/helloworld/async_greeter_server.py
    - https://github.com/grpc/grpc/blob/master/examples/python/hellostreamingworld/async_greeter_server.py
    - https://groups.google.com/g/grpc-io/c/6Yi_oIQsh3w
"""

from concurrent import futures
import logging
import signal
from typing import Any
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter

import grpc
from helloworld_pb2 import HelloRequest, HelloReply
from helloworld_pb2_grpc import MultiGreeterServicer, add_MultiGreeterServicer_to_server

import iris

NUMBER_OF_REPLY = 10

parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument("-p", "--port", default="50051", help="Server port")
args = vars(parser.parse_args())

class Greeter(MultiGreeterServicer):

    def SayHello(self, request: HelloRequest, context) -> HelloReply:
        logging.info("Serving SayHello request %s", request)
        obj = iris.cls("dc.jrpereira.gRPC.HelloWorldServer")._New()
        # à votre code ObjectScript
        return obj.SayHelloObjectScript(request)

    def SayHelloStream(self, request: HelloRequest, context: grpc.aio.ServicerContext) -> HelloReply:
        logging.info("Serving SayHelloStream request %s", request)
        obj = iris.cls("dc.jrpereira.gRPC.HelloWorldServer")._New()
        n = request.num_greetings
        if n == 0:
            n = NUMBER_OF_REPLY
        for i in range(n):
            # à votre code ObjectScript
            yield obj.SayHelloObjectScript(request)

def get_server():
    port = args["port"]
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    add_MultiGreeterServicer_to_server(Greeter(), server)
    listen_addr = f"[::]:{port}"
    server.add_insecure_port(f"[::]:{port}")
    logging.info("Starting server on %s", listen_addr)
    return server

def handle_sigterm(*_: Any) -> None :
    """Shutdown gracefully."""
    done_event = server.stop(None)
    done_event.wait(None)
    print('Stop complete.')

logging.basicConfig(level=logging.INFO)

server = get_server()
server.start()

# https://groups.google.com/g/grpc-io/c/6Yi_oIQsh3w
signal.signal(signal.SIGTERM, handle_sigterm)

server.wait_for_termination()

Comme vous pouvez le voir, ici les méthodes définies dans la spécification protobuf - SayHello() et SayHelloStream(), sont mises en oeuvre.

La méthode SayHello() n'envoie qu'une seule valeur, alors que la méthode SayHelloStream() retourne au client un nombre de messages égal à NUMBER_OF_REPLY, grâce à l'opérateur Python yield.

Notez également que j'ai créé un accrochage pour injecter la logique définie en ObjectScript. Ainsi, j'ai défini une méthode appelée SayHelloObjectScript dans la classe dc.jrpereira.gRPC.HelloWorldServer :

Method SayHelloObjectScript(request)
{
    Set sys = $system.Python.Import("sys")
    Do sys.path.append("/usr/irissys/mgr/python/grpc-test/")

    Set helloworldpb2 = $system.Python.Import("helloworld_pb2")

    Set reply = helloworldpb2.HelloReply()
    Set reply.message = "Hi "_request.name_"! :)"

    Return reply
}

De cette façon, vous pouvez recevoir des demandes de clients gRPC à partir de Python et les traiter en utilisant un mélange de logique codée en Python et ObjectScript..

Mise en œuvre d'un client pour le service

Comme un code client n'a pas besoin de concurrence, je l'ai mis en œuvre en utilisant du code Python directement dans une classe ObjectScript :

ClassMethod ExecutePython() [ Language = python ]
{
    import sys
    sys.path.append('/usr/irissys/mgr/python/grpc-test/')

    import grpc
    from helloworld_pb2 import HelloRequest
    from helloworld_pb2_grpc import MultiGreeterStub

    channel = grpc.insecure_channel('localhost:50051')
    stub = MultiGreeterStub(channel)

    response = stub.SayHello(HelloRequest(name='you'))
    print("Greeter client received: " + response.message)

    for response in stub.SayHelloStream(HelloRequest(name="you")):
        print("Greeter client received from stream: " + response.message)
}

Tout d'abord, nous ajoutons le répertoire grpc-test au chemin Python afin de pouvoir importer le code.

Ensuite, une connexion à localhost sur le port 50051 et un stub (ou client) sont créés.

Avec un tel client, nous pouvons demander des informations sur le serveur qui écoute sur localhost:50051, grâce aux méthodes SayHello() et SayHelloStream().

La méthode SayHello() ne renvoie qu'une seule valeur, il nous suffit donc de faire une requête et d'utiliser sa réponse. En revanche, la méthode SayHelloStream() renvoie un flux de données dans une collection, et nous devons donc le traverser pour obtenir toutes ses données.

Test du code

Ok, maintenant testons notre code.

Vous pouvez consulter tout ce code dans mon projet hello world. Suivez ces étapes pour le faire fonctionner :

git clone https://github.com/jrpereirajr/iris-grpc-example
cd iris-grpc-example
docker-compose up -d

Ensuite, ouvrez un terminal IRIS via le terminal système ou via Visual Studio Code :

docker exec -it iris-grpc-example_iris_1 bash
iris session iris

Lancez notre serveur gRPC :

Set server = ##class(dc.jrpereira.gRPC.HelloWorldServer).%New()
Do server.Start()

Maintenant, créons un client gRPC pour intéragir avec ce serveur :

Set client = ##class(dc.jrpereira.gRPC.HelloWorldClient).%New()
Do client.ExecutePython()

Si tout est OK, vous devriez voir un tas de messages d'accueil dans le terminal.

Enfin, arrêtons le serveur :

Do server.Stop()

Utilisation de l'utilitaire grpcurl dans notre hello world

Comme je l'ai déjà dit, l'utilitaire grpcurl est un équivalent de curl, mais ici au lieu d'agir comme un client HTTP (comme curl), nous utilisons grpcurl comme un client gRPC pour tester les services d'un serveur gRPC en fonctionnement. Donc, utilisons-le pour jouer un peu plus avec notre hello world.

Tout d'abord, téléchargeons et installons l'utilitaire grpcurl :

cd /tmp
wget https://github.com/fullstorydev/grpcurl/releases/download/v1.8.6/grpcurl_1.8.6_linux_x86_64.tar.gz
tar -zxvf grpcurl_1.8.6_linux_x86_64.tar.gz

Vérifiez si l'installation est correcte, en tapant :

./grpcurl --help

Si tout est OK, vous devriez recevoir un résultat avec toutes les options grpcurl.

Maintenant, demandons quels services sont disponibles sur le serveur :

./grpcurl \
    -plaintext \
    -import-path /irisrun/repo/jrpereira/python/grpc-test \
    -proto helloworld.proto \
    localhost:50051 \
    list

Vous devriez recevoir la réponse suivante :

helloworld.MultiGreeter

Comme vous pouvez le voir, l'utilitaire a retourné notre service défini dans le fichier proto (helloworld.MultiGreeter) sous forme de réponse pour lister tous les services disponibles .

Dans la commande ci-dessus, j'ai mis chaque paramètre dans une ligne séparée. Donc, nous allons expliquer chacun d'eux :

-plaintext: permet d'utiliser gRPC sans TLS (mode non sécurisé) ; nous l'utilisons ici car nous n'avons pas implémenté de connexion sécurisée pour notre serveur. Bien entendu, ce mode ne doit être utilisé que dans un environnement de non-production
-import-path et -proto : chemin et nom du fichier .proto (définition du service) ; nécessaire si le serveur n'implémente pas la réflexion

Après ces paramètres, nous fournissons le nom d'hôte et le port du serveur, et ensuite une commande grpcurl - list dans ce cas.

Maintenant, demandons toutes les méthodes du service helloworld.MultiGreeter :

./grpcurl \
    -plaintext \
    -import-path /irisrun/repo/jrpereira/python/grpc-test \
    -proto helloworld.proto \
    localhost:50051 \
    list helloworld.MultiGreeter

Vous devriez recevoir le message suivant :

helloworld.MultiGreeter.SayHello
helloworld.MultiGreeter.SayHelloStream

Comme vous pouvez le constater, ce sont les méthodes définies dans le fichier proto utilisé pour générer le code de notre serveur.

Ok, maintenant testons la méthode SayHello() :

./grpcurl \
    -plaintext  \
    -d '{"name":"you"}' \
    -import-path /irisrun/repo/jrpereira/python/grpc-test \
    -proto helloworld.proto \
    localhost:50051 \
    helloworld.MultiGreeter.SayHello

Voici le résultat attendu (tout comme celui que notre client a mis en œuvre précédemment) :

{
  "message": "Salut! :)"
}

Testons également l'autre méthode, SayHelloStream() :

./grpcurl \
    -plaintext -d '{"name":"you"}' \
    -import-path /irisrun/repo/jrpereira/python/grpc-test \
    -proto helloworld.proto localhost:50051 \
    helloworld.MultiGreeter.SayHelloStream

Et, nous devrions avoir un flux avec 10 messages d'accueil :

{
  "message": "Salut! :)"
}
{
  "message": "Salut! :)"
}
...
{
  "message": "Salut! :)"
}

Enfin, faisons un léger changement sur cette commande pour utiliser une autre propriété dans le message protobuf, la propriété num_greetings. Cette propriété est utilisée par le serveur pour contrôler le nombre de messages qui seront envoyés dans le flux.

Ainsi, cette commande demande au serveur de ne renvoyer que 2 messages dans le flux, au lieu des 10 par défaut :

./grpcurl \
    -plaintext -d '{"name":"you", "num_greetings":2}' \
    -import-path /irisrun/repo/jrpereira/python/grpc-test \
    -proto helloworld.proto localhost:50051 \
    helloworld.MultiGreeter.SayHelloStream

Et c'est ce que vous devriez voir dans le terminal :

{
  "message": "Salut! :)"
}
{
  "message": "Salut! :)"
}

Conclusion

Dans cet article, un aperçu de gRPC a été donné, avec ses avantages et ses inconvénients - principalement par rapport à REST. De plus, quelques exemples de son utilisation avec IRIS ont été testés, en adaptant certains échantillons pour Python, présentés dans le répertoire officiel de gRPC.

Comme nous l'avons dit précédemment, gRPC a été utilisé dans certains cas, et l'interopérabilité est l'un des cas possibles. La création d'un adaptateur d'interopérabilité IRIS est donc une façon logique de penser à une utilisation pratique de gRPC dans IRIS.

Cependant, cela demandera plus d'efforts, donc ce sera le sujet d'un autre article. =)

J'espère que vous avez trouvé les informations présentées ici utiles ! A bientôt !

Références

https://grpc.io/docs/what-is-grpc/introduction/
https://developers.google.com/protocol-buffers
https://en.wikipedia.org/wiki/GRPC
https://www.imaginarycloud.com/blog/grpc-vs-rest/
https://www.vineethweb.com/post/grpc/
https://www.capitalone.com/tech/software-engineering/grpc-framework-for-...
https://www.altexsoft.com/blog/what-is-grpc/

Discussion (0)2
Connectez-vous ou inscrivez-vous pour continuer