Introduction à gRPC

Introduction à la gRPC

1. introduction

gRPC is a high performance, open source RPC framework initially developed by Google. Il aide à éliminer le code standard et aide à connecter les services polyglottes dans et entre les centres de données.

2. Vue d'ensemble

L'infrastructure est basée sur un modèle client-serveur d'appels de procédures distantes. A client application can directly call methods on a server application as if it was a local object.

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 le code serveur et client à l'aide du compilateur de tampon de protocole

  3. Créer l'application serveur en implémentant les interfaces de service générées et en générant le serveur gRPC

  4. Créer l'application cliente, passer des appels RPC à l'aide des stubs générés

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

3. Dépendances Maven

Ajoutons les dépendancesgrpc-netty,grpc-protobuf etgrpc-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,specifying methods that can be called remotely along with their parameters and return types.

Ceci est fait dans le fichier.proto en utilisant lesprotocol buffers. Ils sont également utilisés pour décrire la structure des messages de charge utile.

4.1. Configurations de base

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

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

La première ligne indique au compilateur quelle syntaxe est 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 paquet 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;
}

Ceci définit la charge de la requête. Ici, chaque attribut entrant dans le message est défini avec son type.

Un numéro unique doit être attribué à chaque attribut, appelé balise. This tag is used by the protocol buffer to represent the attribute instead of using the attribute name.

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

Notez que nous pouvons utiliser la même balise sur plusieurs types de message:

message HelloResponse {
    string greeting = 1;
}

4.3. Définition du contrat de service

Enfin, définissons le contrat de service. Pour nosHelloService, nous définissons une opérationhello():

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

L'opérationhello() 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érer le code

Maintenant, nous passons le fichierHelloService.proto au compilateur de tampon de protocoleprotoc pour générer les fichiers Java. Il y a plusieurs façons de déclencher cela.

5.1. Utilisation du compilateur Protocol Buffer

Download the compiler et suivez les instructions du fichier README.

Vous pouvez utiliser la commande suivante pour générer le code:

protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/HelloService.proto

5.2. Utilisation du plugin Maven

En tant que développeur, vous souhaitez que la génération de code soit étroitement intégrée à votre système de construction. gRPC fournit unprotobuf-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 / pluginos-maven-plugin génère diverses propriétés de projet utiles dépendant de la plate-forme comme$\{os.detected.classifier}

6. Création du serveur

Quelle que soit la méthode utilisée pour la génération de code, les fichiers de clé suivants sont générés:

  • HelloRequest.java – contient la définition de typeHelloRequest

  • HelloResponse.java *–* ceci contient la définition de typeHelleResponse

  • HelloServiceImplBase.java *–* ceci contient la classe abstraiteHelloServiceImplBase 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

Lesdefault implementation of the abstract class HelloServiceImplBase is to throw runtime exceptionio.grpc.StatusRuntimeException indiquent que la méthode n'est pas implémentée.

Nous allons étendre cette classe et remplacer la méthodehello() mentionnée dans notre définition de service:

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();
    }
}

Si nous comparons la signature dehello() avec celle que nous avons écrite dans le fichierHellService.proto, nous remarquerons qu'elle ne renvoie pasHelloResponse. Au lieu de cela, il prend le deuxième argument commeStreamObserver<HelloResponse>, qui est un observateur de réponse, un rappel que le serveur appelle avec sa réponse.

De cette façonthe client gets an option to make a blocking call or a non-blocking call.

gRPC utilise des générateurs pour créer des objets. Nous utilisonsHelloResponse.newBuilder() et définissons le texte de bienvenue pour créer un objetHelloResponse. Nous définissons cet objet sur la méthodeonNext() de responseObserver pour l’envoyer au client.

Enfin, nous devons appeleronCompleted() pour spécifier que nous avons fini de traiter le RPC, sinon la connexion sera interrompue et le client attendra simplement que plus d'informations arrivent.

6.2. Exécution du serveur Grpc

Ensuite, nous devons démarrer le serveur gRPC pour écouter les demandes entrantes:

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

        server.start();
        server.awaitTermination();
    }
}

Ici encore, nous utilisons le générateur pour créer un serveur gRPC sur le port 8080 et ajouter le serviceHelloServiceImpl que nous avons défini. start() démarrerait le serveur. Dans notre exemple, nous appelleronsawaitTermination() pour que le serveur continue de fonctionner au premier plan en bloquant l'invite.

7. Création du client

gRPC provides a channel construct which abstracts out the underlying details comme la connexion, le regroupement de connexions, l'équilibrage de charge, etc.

Nous allons créer une chaîne en utilisantManagedChannelBuilder. Ici, nous spécifions l'adresse du serveur et le port.

Nous utiliserons du texte brut sans aucun chiffrement:

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("example")
          .setLastName("gRPC")
          .build());

        channel.shutdown();
    }
}

Ensuite, nous devons créer un stub que nous utiliserons pour effectuer l’appel distant réel vershello(). The stub is the primary way for clients to interacts with the server. Lorsque vous utilisez la génération automatique de stubs, la classe de stub aura des constructeurs pour encapsuler le canal.

Ici, nous utilisons un stub bloquant / synchrone pour que l'appel RPC attende que le serveur réponde et renvoie une réponse ou déclenche une exception. Il existe deux autres types de stubs fournis par gRPC, qui facilitent les appels non bloquants / asynchrones.

Enfin, il est temps de faire l'appel RPC dehello(). Ici, nous passons lesHelloRequest. Nous pouvons utiliser les paramètres générés automatiquement pour définir les attributsfirstName,lastName de l'objetHelloRequest.

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

8. Conclusion

Dans ce tutoriel, 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 passe-partout.

Comme d'habitude, vous trouverez les sourcesover on GitHub.