Introduction à gRPC

1. Introduction

gRPC est un framework RPC open source haute performance initialement développé par Google. Il aide à éliminer le code standard et aide à connecter les services polyglottes dans et entre les centres de données.

2. Aperçu

Le framework est basé sur un modèle client-serveur d'appels de procédure distants. Une application cliente peut appeler directement des méthodes sur une application serveur comme s'il s'agissait d'un objet local.

Cet article utilisera les étapes suivantes pour créer une application client-serveur typique à l'aide de gRPC:

  1. Définir un service dans un fichier .proto
  2. Générer du code serveur et client à l'aide du compilateur de tampon de protocole
  3. Créer l'application serveur, implémenter les interfaces de service générées et générer le serveur gRPC
  4. Créer l'application cliente en effectuant des appels RPC à l'aide de stubs générés

Définissons un simple HelloService qui renvoie des salutations en échange du prénom et du nom.

3. Dépendances de Maven

Ajoutons les dépendances grpc-netty, grpc-protobuf et grpc-stub:

 io.grpc grpc-netty 1.16.1   io.grpc grpc-protobuf 1.16.1   io.grpc grpc-stub 1.16.1  

4. Définition du service

Nous commençons par définir un service, en spécifiant les méthodes qui peuvent être appelées à distance avec leurs paramètres et leurs types de retour .

Cela se fait dans le fichier .proto en utilisant les tampons de protocole. Ils sont également utilisés pour décrire la structure des messages de charge utile.

4.1. Configurations de base

Créons un fichier HelloService.proto pour notre exemple HelloService . Nous commençons par ajouter quelques détails de configuration de base:

syntax = "proto3"; option java_multiple_files = true; package org.baeldung.grpc;

La première ligne indique au compilateur la syntaxe utilisée dans ce fichier. Par défaut, le compilateur génère tout le code Java dans un seul fichier Java. La deuxième ligne remplace ce paramètre et tout sera généré dans des fichiers individuels.

Enfin, nous spécifions le package que nous voulons utiliser pour nos classes Java générées.

4.2. Définition de la structure des messages

Ensuite, nous définissons le message:

message HelloRequest { string firstName = 1; string lastName = 2; }

Cela définit la charge utile de la demande. Ici, chaque attribut qui entre dans le message est défini avec son type.

Un numéro unique doit être attribué à chaque attribut, appelé comme balise. Cette balise est utilisée par le tampon de protocole pour représenter l'attribut au lieu d'utiliser le nom d'attribut.

Ainsi, contrairement à JSON où nous passerions le nom d'attribut firstName à chaque fois, le tampon de protocole utiliserait le numéro 1 pour représenter firstName . La définition de la charge utile de réponse est similaire à la demande.

Notez que nous pouvons utiliser la même balise dans plusieurs types de messages:

message HelloResponse { string greeting = 1; }

4.3. Définition du contrat de service

Enfin, définissons le contrat de service. Pour notre HelloService, nous définissons une opération hello () :

service HelloService { rpc hello(HelloRequest) returns (HelloResponse); }

L'opération hello () accepte une requête unaire et renvoie une réponse unaire. gRPC prend également en charge le streaming en ajoutant le mot-clé stream à la demande et à la réponse.

5. Génération du code

Maintenant , nous passons le HelloService.proto fichier au compilateur tampon protocole protoc pour générer les fichiers Java. Il existe plusieurs façons de déclencher cela.

5.1. Utilisation du compilateur Protocol Buffer

Tout d'abord, nous avons besoin du compilateur Protocol Buffer. Nous pouvons choisir parmi de nombreux binaires précompilés disponibles ici.

De plus, nous devons obtenir le plugin gRPC Java Codegen.

Enfin, nous pouvons utiliser la commande suivante pour générer le code:

protoc --plugin=protoc-gen-grpc-java=$PATH_TO_PLUGIN -I=$SRC_DIR --java_out=$DST_DIR --grpc-java_out=$DST_DIR $SRC_DIR/HelloService.proto

5.2. Utilisation du plugin Maven

En tant que développeur, vous voudriez que la génération de code soit étroitement intégrée à votre système de construction. gRPC fournit un protobuf-maven-plugin pour le système de construction Maven:

   kr.motd.maven os-maven-plugin 1.6.1     org.xolstice.maven.plugins protobuf-maven-plugin 0.6.1   com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}  grpc-java  io.grpc:protoc-gen-grpc-java:1.4.0:exe:${os.detected.classifier}      compile compile-custom      

L'extension / plugin os-maven-plugin génère diverses propriétés de projet utiles dépendant de la plate-forme, telles que $ {os.detected.classifier}

6. Création du serveur

Quelle que soit la méthode que vous utilisez pour la génération de code, les fichiers clés suivants seront générés:

  • HelloRequest.java - contient la définition de type HelloRequest
  • HelloResponse.java - contient la définition de type HelleResponse
  • HelloServiceImplBase.java - il contient la classe abstraite HelloServiceImplBase qui fournit une implémentation de toutes les opérations que nous avons définies dans l'interface de service

6.1. Remplacer la classe de base de service

L' implémentation par défaut de la classe abstraite HelloServiceImplBase consiste à lever l' exception d'exécution io.grpc.StatusRuntimeException indiquant que la méthode n'est pas implémentée.

We shall extend this class and override the hello() method mentioned in our service definition:

public class HelloServiceImpl extends HelloServiceImplBase { @Override public void hello( HelloRequest request, StreamObserver responseObserver) { String greeting = new StringBuilder() .append("Hello, ") .append(request.getFirstName()) .append(" ") .append(request.getLastName()) .toString(); HelloResponse response = HelloResponse.newBuilder() .setGreeting(greeting) .build(); responseObserver.onNext(response); responseObserver.onCompleted(); } }

If we compare the signature of hello() with the one we wrote in the HellService.proto file, we'll notice that it does not return HelloResponse. Instead, it takes the second argument as StreamObserver, which is a response observer, a call back for the server to call with its response.

This way the client gets an option to make a blocking call or a non-blocking call.

gRPC uses builders for creating objects. We use HelloResponse.newBuilder() and set the greeting text to build a HelloResponse object. We set this object to the responseObserver's onNext() method to send it to the client.

Finally, we need to call onCompleted() to specify that we’ve finished dealing with the RPC, else the connection will be hung, and the client will just wait for more information to come in.

6.2. Running the Grpc Server

Next, we need to start the gRPC server to listen for incoming requests:

public class GrpcServer { public static void main(String[] args) { Server server = ServerBuilder .forPort(8080) .addService(new HelloServiceImpl()).build(); server.start(); server.awaitTermination(); } }

Here, again we use the builder to create a gRPC server on port 8080 and add the HelloServiceImpl service that we defined. start() would start the server. In our example, we will call awaitTermination() to keep the server running in the foreground blocking the prompt.

7. Creating the Client

gRPC provides a channel construct that abstracts out the underlying details like connection, connection pooling, load balancing, etc.

We'll create a channel using ManagedChannelBuilder. Here, we specify the server address and port.

We'll be using plain text without any encryption:

public class GrpcClient { public static void main(String[] args) { ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8080) .usePlaintext() .build(); HelloServiceGrpc.HelloServiceBlockingStub stub = HelloServiceGrpc.newBlockingStub(channel); HelloResponse helloResponse = stub.hello(HelloRequest.newBuilder() .setFirstName("Baeldung") .setLastName("gRPC") .build()); channel.shutdown(); } }

Next, we need to create a stub which we'll use to make the actual remote call to hello(). The stub is the primary way for clients to interacts with the server. When using auto-generated stubs, the stub class will have constructors for wrapping the channel.

Here we're using a blocking/synchronous stub so that the RPC call waits for the server to respond, and will either return a response or raise an exception. There are two other types of stubs provided by gRPC, which facilitate non-blocking/asynchronous calls.

Enfin, il est temps de faire l' appel RPC hello () . Ici, nous passons le HelloRequest . Nous pouvons utiliser les setters générés automatiquement pour définir les attributs firstName , lastName de l' objet HelloRequest .

Nous récupérons l' objet HelloResponse renvoyé par le serveur.

8. Conclusion

Dans ce didacticiel, nous avons vu comment nous pourrions utiliser gRPC pour faciliter le développement de la communication entre deux services en nous concentrant sur la définition du service et en laissant le gRPC gérer tout le code standard.

Comme d'habitude, vous trouverez les sources sur GitHub.