Série : gRPC – Démonstration

gRPC est développé par la société Google c’est un Framework permettant de facilité la communication inter-plateforme dans des systèmes distribués.

Introduction

Nous allons ici entrée dans le vif du sujet avec un exemple d’utilisation de gRPC et en utilisant dans un premier temps le langage GO, donc je commence l’étude et que vous pouvez retrouver dans des billets dédiés à l’étude de ce langage de Google puissant et agréable.

Prérequis

Nous allons ici entrée dans le vif du sujet avec un exemple d’utilisation de gRPC et en utilisant dans un premier temps le langage GO, donc je commence l’étude et que vous pouvez retrouver dans des billets dédiés à l’étude de ce langage de Google puissant et agréable.

Environnement de développement

Pour ma part, j’utilise le plus simplement du monde les outils suivants :

  • Visual Code ;
  • La dernière version du SDK de GO ;
  •  La dernière version du compilateur Protocol Buffers ;
  •  Et l’incontournable Terminal pour les lignes de commande (dans mon cas dans Visual Code).

Pour la compilation des proto en GO puis en gRPC pour GO, il faudra aussi installer :

  • google.golang.org/protobuf/cmd/protoc-gen-go ;
  • google.golang.org/protobuf/cmd/protoc-gen-go-grpc ;
  • google.golang.org/grpc/codes ;
  • google.golang.org/grpc/status ;
  • google.golang.org/grpc ;

Démo

Cette démo basique va permettre d’introduire les différents concept suivant :

  • la définition de notre service au travers d’un fichier à l’aide de l’IDL proto ;
  • la génération le code de notre serveur et le code qui sera utilisé par le client en GO ;
  • de générer le code spécifique à gRPC à partir du code généré à partir de l’IDL proto ;
  • l’implémentation de l’interface, du serveur gRPC ;
  • la consommation du service au travers d’appel à partir de notre client écrit en GO.

Le but fonctionnel de la démo n’a pas vraiment d’intérêt c’est surtout l’implémentation, la consommation de celui-ci. Donc pour faire simple je suis partie sur un service mettant à disposition un ensemble d’opérations de calcul basique, tels que : l’addition, soustraction et retourner une table de multiplication quelque conque.

Définition de notre “Protocol buffers”

Description

Ici, nous allons écrire l’ensemble des descriptions des messages et des opérations de notre service à l’aide de se langage de description. Il va nous permettre de générer le code servant côté serveur mais aussi le code servant côté client que l’on peut appeler le contrat de service.

Code

syntax = "proto3";

    option go_package = "fredericschmidt.fr/GoDemo01/calculatrice";

    package Calculator;

    service CalculatorService {
        rpc AddOperation (ValuesCalculatorRequest) returns (ResultCalculatorResponse);
        rpc SubOperation (ValuesCalculatorRequest) returns (ResultCalculatorResponse);
        rpc TableOperation (TableCalculatorRequest) returns (TableCalculatorResponse);
    }

    message ResultCalculatorResponse {
        int32 Result = 1;
    }

    message ValuesCalculatorRequest {
        int32 TermX = 1;
        int32 TermY = 2;
    }

    message TableCalculatorRequest {
        int32 Multiplicand = 1;
        int32 Multiplier = 2;
    }

    message TableCalculatorResponse {
        repeated OneLineInTableResponse LineOfTable = 1;
    }

    message OneLineInTableResponse {
        int32 Multiplicand  = 1;
        int32 Multiplier = 2;
        int32 Product = 3;
    }

Génération

1ere Etape

Après, il faut générer le code en Langage GO dans notre cas et pour ce faire nous allons exécuter la commande suivante :

protoc --go_out="D:\TempDev\Lab\gRpc\goroot\src" .\Calculatrice.proto

Alors la commande protoc doit si l’installation a bien été effectuée dans le répertoire de la variable GOBIN.

A ce même titre on devrait retrouver aussi les commandes protoc-gen-go.exe et protoc-gen-go-grpc.exe. Quand la génération a bien été effectuée à l’aide de la commande protoc, qui prend deux paramètres qui se trouvent être :

  • –go_out : qui est le chemin racine où le générateur va créer l’arborescence de la variable proto go_package 
    en l’occurrence la racine des sources de la variable GOPATH. Puisqu’avec GO nous parlons de package et de son arborescence (voir le billet la structuration d’un projet) ; 
  • le dernier paramètre étant le répertoire et le nom de fichier proto.
2nde Etape

Après, il nous faut générer à partir de l’étape précédence le fichier en langage GO spécifique à gRPC et pour ce faire nous allons utiliser la commande suivante :

protoc --go-grpc_out="D:\TempDev\Lab\gRpc\goroot\src" .\Calculatrice.proto

avec pour paramètres :

  • –go-grpc_out : qui est le chemin racine où le générateur va créer l’arborescence de la variable
    proto go_package en l’occurrence la racine des sources de la variable GOPATH. Puisqu’avec GO nous parlons de package et de son arborescence (voir le billet la structuration d’un projet) ; 
  • le dernier paramètre étant le répertoire et le nom de fichier proto.

Nous obtenons à l’issue de ces deux étapes deux fichiers en GO :

  • calculatrice_grpc_pb.go ;
  • calculatrice.pb.go.

Nous reviendrons plus en détails sur le contenu de ces deux fichiers ultérieurement.

Résumé

Pour pouvoir utiliser le code généré auparavant il nous faut développer deux parties distinctes :

  • la partie serveur : qui va écouter, répondre aux demandes et implémenter le contrat et 
    effectuer les traitements en fonction des méthodes du contrat ;
  • la partie cliente : qui va effectuer les appels au serveur et restituer les réponses en fonction
    du contrat de service exposé par la partie serveur.

Le Serveur

GitHub

Pour le code de l’implémentation, je vous laisse le soin de regarder directement dans les sources qui seront disponibles sur mon GitHub.

Explication de code

Dans notre démonstration, la partie serveur va implémenter les méthodes suivantes :

type CalculatorServiceServer interface {
        AddOperation(context.Context, *ValuesCalculatorRequest) (*ResultCalculatorResponse, error)
        SubOperation(context.Context, *ValuesCalculatorRequest) (*ResultCalculatorResponse, error)
        TableOperation(context.Context, *TableCalculatorRequest) (*TableCalculatorResponse, error)
    	mustEmbedUnimplementedCalculatorServiceServer()
    }

L’étape suivante de démarrer et mettre en écoute le  serveur gRPC et pour cela nous allons utiliser le package GO
concernant le gRPC.

Nous allons donc l’installer à l’aide de la commande GO suivante :

go get google.golang.org/grpc  

Voici le code de la partie serveur :

fmt.Println("Starting Calculator Server gRPC")

	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("ERROR : failed to listen on port %d : %v", port, err)
	}

	srv := grpc.NewServer()
	myPb.RegisterCalculatorServiceServer(srv, &CalculatorServer{})
	if err := srv.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}

Quelques explications sur certaines lignes du code.

lis, err := net.Listen("tcp", port)

Permet de mettre sur écoute toutes les adresses disponibles sur la machine (car nous n’avons pas précisé d’adresse)
locale sur un port précis dans un protocole défini et d’attendre les demandes.

srv := grpc.NewServer()

Demande à gRPC de créer un serveur ou plutôt une coquille vide, car il n’y aura pas encore de service défini et il ne
sera pas démarrer. Et donc ne pourra pas encore accepter des demandes et les traiter.

myPb.RegisterCalculatorServiceServer(srv, &CalculatorServer{})

Si l’on regarde le code généré à partir de l’IDL, on voit quelle fait appel à la ligne de suivante :

s.RegisterService(&_CalculatorService_serviceDesc, srv)

Elle permet de déclarer et d’enregistrer pour notre coquille vide de serveur le contrat de service et son implémentation tout ceci étant contenu dans le code généré à partir de l’IDL. C’est indispensable de faire cela avant toutes choses. 

La dernière étape est le démarrage du serveur pour qu’il puisse traiter les demandes. Voici la partie de code avec une écriture spécifique à GO dans la condition :

if err := srv.Serve(lis); err != nil { 
        log.Fatalf("failed to serve: %v", err)
}

L’instruction qui nous intéresse est la suivante :

err := srv.Serve(lis);

Elle va simplement se mettre à l’écoute sur notre listener et traiter les demandes entrantes. Chaque demande (Transport et service) sera traitée dans une Go Routine (voir le billet sur le langage GO). Si une erreur fatal survient lors d’une demande alors le serveur sera fermé et une fatale error sera remontée.

Pour notre démonstration, pour stopper le serveur il faudra tout simplement effectuer un <CTRL>+<C>.

Le client

GitHub

Pour le code de l’implémentation, je vous laisse le soin de regarder directement dans les sources qui seront disponibles sur mon GitHub.

Explication de code

Je ne vais m’arrêter que sur les parties de code propre à gRPC, le reste étant plus à lire dans les billets qui sont sur le langage GO et ses spécificités. En particulier, sur l’utilisation du package flag pour le traitement des arguments en ligne de commande.

cnx, err := grpc.Dial(uri, grpc.WithInsecure(), grpc.WithBlock())

Etablie une connexion vers un serveur se trouvant à l’adresse uri, dans notre cas se sera localhost:666 et avec les options suivantes :

  • WithInsecure() : pour désactiver la sécurité au niveau transport ;
  • WithBlock() : pour demander que la commande soit bloquante, car si cela n’est pas préciser 
    l’instruction rend la main tout de suite et la connexion se fait en arrière plan.
    Ici on veut passer à la suite que si la connexion a été effectuée ou une erreur.
c := pb.NewCalculatorServiceClient(cnx)

On instancie un client on pourrait aussi parler de proxy dans d’autres langages, vers notre serveur puisque l’on a établie une connexion cnx.

ctx, cancel := context.WithTimeout(context.Background(), time.Second)

Permet de mettre en place un Timeout si notre traitement dans notre contexte principal, dans notre cas, notre func main passe la seconde. Si cela dépasse une erreur de type DeadlineExceed interrompra l’appel en cours. Vous pouvez simplement le vérifier en mettant time.Microsecond en lieu et place de time.Second.

defer cnx.Close()

Fermeture de la connexion après la fin du traitement. On utilise ici un mot clé du langage GO defer qui permet de prévoir les instructions à effectuer lorsque l’on sortira de la func et c’est GO qui prendra en charge la ou les instructions.

Utilisation du client

.\ClientGoDemo01.exe -action TABLE -Multiplicand 5 -Multiplier 10

Cette commande, après avoir compiler les deux parties client et serveur. Après avoir démarrer la partie serveur et quelle soit en écoute, qui est exécutée en ligne de commande quelque conque. Permet de demander au serveur de nous retourner la table de multiplication des 5, de 0 à 10 (toute notre enfance les tables de multiplications !!). Le résultat en sortie donnera ceci :

Client started
Table de multiplation de 5 X 10.
5 X 0 = 0
5 X 1 = 5
5 X 2 = 10
5 X 3 = 15
5 X 4 = 20
5 X 5 = 25
5 X 6 = 30
5 X 7 = 35
5 X 8 = 40
5 X 9 = 45
5 X 10 = 50
Client ended.

Si vous avez besoin de l’aide en ligne de la commande il suffit de taper la commande suivante :

.\ClientGoDemo01.exe --help

En résumé

Ici nous venons de voir :

  • l’ouverture du connexion client ;
  • l’instanciation d’un proxy ;
  • l’appel d’une méthode vers le serveur qui pour le client est transparente ;
  • la mise en place d’un timeout sur le temps d’exécution de la commande ;
  • la fermeture de la connexion.

Conclusion

Voilà, un exemple basique de mise en place d’un serveur gRPC et de sa consommation au travers d’un client En utilisant l’IDL pour le protocole de buffers pour générer le service, les messages, le contrat de service.

Références

Source du projet sur GitHub

Voici quelques références sur le sujet gRPC et Protocol Buffers de Google.

Site Officiel du gRPC

Site Officiel du Protocol Buffers

GitHub du transpilateur

GitHub de l’extension de Google pour transpiler en langage GO

Billets sur le langage GO

Frédéric Schmidt

Lead Technical Architect.

Ajouer un commentaire

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

Recent Comments

    %d blogueurs aiment cette page :