Créer un service gRPC avec Java

1. Présentation

gRPC est un framework et un ensemble d'outils d'appel de procédure à distance (RPC) indépendants du langage et de la plate-forme, développés par Google. Il vous permet de définir un service à l'aide de Protocol Buffers, un ensemble d'outils et un langage de sérialisation binaires particulièrement puissants. Il vous permet ensuite de générer des bouchons de client et de serveur idiomatiques à partir de la définition de votre service dans plusieurs langages.

Dans cet atelier de programmation, vous allez apprendre à créer un service Java qui expose une API à l'aide du framework gRPC, puis à écrire un client pour utiliser le bouchon gRPC généré côté client.

Points abordés

  • Le langage de tampon de protocole
  • Mettre en œuvre un service gRPC à l'aide de Java
  • Implémenter un client gRPC à l'aide de Java

Comment allez-vous utiliser ce tutoriel ?

Je vais le lire uniquement Je vais le lire et effectuer les exercices

Comment évalueriez-vous votre expérience en matière de création d'applications Node.js ?

Débutant Intermédiaire Expert

Comment évalueriez-vous votre expérience en matière de création d'applications Go ?

<ph type="x-smartling-placeholder"></ph> Débutant Intermédiaire Expert
.

2. Préparation

Configuration de l'environnement d'auto-formation

  1. Connectez-vous à la console Cloud, puis créez un projet ou réutilisez un projet existant. (Si vous ne possédez pas encore de compte Gmail ou Google Workspace, vous devez en créer un.)

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

Mémorisez l'ID du projet. Il s'agit d'un nom unique permettant de différencier chaque projet Google Cloud (le nom ci-dessus est déjà pris ; vous devez en trouver un autre). Il sera désigné par le nom PROJECT_ID tout au long de cet atelier de programmation.

  1. Vous devez ensuite activer la facturation dans Cloud Console pour pouvoir utiliser les ressources Google Cloud.

L'exécution de cet atelier de programmation est très peu coûteuse, voire gratuite. Veillez à suivre les instructions de la section "Nettoyer" qui indique comment désactiver les ressources afin d'éviter les frais une fois ce tutoriel terminé. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai sans frais pour bénéficier d'un crédit de 300$.

Google Cloud Shell

Cet atelier de programmation peut être exécuté sur votre ordinateur. Toutefois, dans cet atelier de programmation, nous allons utiliser Google Cloud Shell, un environnement de ligne de commande exécuté dans le cloud.

Cette machine virtuelle basée sur Debian contient tous les outils de développement dont vous aurez besoin. Elle intègre un répertoire d'accueil persistant de 5 Go et s'exécute sur Google Cloud, ce qui améliore nettement les performances du réseau et l'authentification. Cela signifie que tout ce dont vous avez besoin pour cet atelier de programmation est un navigateur (oui, tout fonctionne sur un Chromebook).

  1. Pour activer Cloud Shell à partir de Cloud Console, cliquez simplement sur Activer Cloud Shell a8460e837e9f5fda.png (l'opération de provisionnement et la connexion à l'environnement ne devraient prendre que quelques minutes).

b532b2f19ab85dda.png

Capture d&#39;écran du 2017-06-14 à 10.13.43 PM.png

Une fois connecté à Cloud Shell, vous êtes normalement déjà authentifié et le projet PROJECT_ID est sélectionné :

gcloud auth list

Résultat de la commande

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

Résultat de la commande

[core]
project = <PROJECT_ID>

Si, pour une raison quelconque, le projet n'est pas défini, exécutez simplement la commande suivante :

gcloud config set project <PROJECT_ID>

Vous recherchez votre PROJECT_ID ? Vérifiez l'ID que vous avez utilisé pendant les étapes de configuration ou recherchez-le dans le tableau de bord Cloud Console :

2485e00c1223af09.png

Par défaut, Cloud Shell définit certaines variables d'environnement qui pourront s'avérer utiles pour exécuter certaines commandes dans le futur.

echo $GOOGLE_CLOUD_PROJECT

Résultat de la commande

<PROJECT_ID>
  1. Pour finir, définissez la configuration du projet et de la zone par défaut :
gcloud config set compute/zone us-central1-f

Vous pouvez choisir parmi différentes zones. Pour en savoir plus, consultez la page Régions et zones.

3. Créer un service gRPC

Créez un projet Java avec Maven:

$ mvn archetype:generate -DgroupId=com.example.grpc \
 -DartifactId=grpc-hello-server \
 -DarchetypeArtifactId=maven-archetype-quickstart \
 -DinteractiveMode=false
$ cd grpc-hello-server

Ajouter un fichier de définition gRPC

Dans gRPC, les charges utiles du service (requête et réponse) et les opérations de service doivent être capturées dans un langage de définition d'interface (IDL, Interface Definition Language). gRPC utilise la syntaxe Protobuffer 3 pour définir les charges utiles et les opérations des messages. Créons un fichier proto pour un service Greeting simple avec une requête Hello et une réponse Hello.

Tout d'abord, créez un répertoire proto qui contiendra le nouveau fichier proto:

$ mkdir -p src/main/proto

Ensuite, créez un fichier proto src/main/proto/GreetingService.proto.

Vous pouvez utiliser vim,nano, ou emacs pour modifier le fichier:

src/main/proto/GreetingService.proto

syntax = "proto3";
package com.example.grpc;

// Request payload
message HelloRequest {
  // Each message attribute is strongly typed.
  // You also must assign a "tag" number.
  // Each tag number is unique within the message.
  string name = 1;

  // This defines a strongly typed list of String
  repeated string hobbies = 2;

  // There are many more basics types, like Enum, Map
  // See https://developers.google.com/protocol-buffers/docs/proto3
  // for more information.
}

message HelloResponse {
  string greeting = 1;
}

// Defining a Service, a Service can have multiple RPC operations
service GreetingService {
  // Define a RPC operation
  rpc greeting(HelloRequest) returns (HelloResponse);
}

Ajouter un plug-in et des dépendances gRPC

Une fois que vous disposez de la définition, nous pouvons générer à la fois le bouchon côté serveur et le bouchon côté client à partir de ce fichier. Vous devez ajouter les dépendances et plug-ins gRPC.

Tout d'abord, ajoutez les dépendances gRPC au fichier pom.xml:

pom.xml

<project>
  ...
  <dependencies>
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-netty-shaded</artifactId>
      <version>1.24.0</version>
    </dependency>
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-protobuf</artifactId>
      <version>1.24.0</version>
    </dependency>
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-stub</artifactId>
      <version>1.24.0</version>
    </dependency>
    <dependency>
      <groupId>javax.annotation</groupId>
      <artifactId>javax.annotation-api</artifactId>
      <version>1.3.2</version>
    </dependency>
    ...
  </dependencies>
  ...
</project>

Ajoutez ensuite le plug-in:

pom.xml

<project>
  ...
  <dependencies>
    ...
  </dependencies>
  <build>
    <extensions>
      <extension>
        <groupId>kr.motd.maven</groupId>
        <artifactId>os-maven-plugin</artifactId>
        <version>1.6.2</version>
      </extension>
    </extensions>
    <plugins>
      <plugin>
        <groupId>org.xolstice.maven.plugins</groupId>
        <artifactId>protobuf-maven-plugin</artifactId>
        <version>0.6.1</version>
        <configuration>
          <protocArtifact>com.google.protobuf:protoc:3.9.0:exe:${os.detected.classifier}</protocArtifact>
          <pluginId>grpc-java</pluginId>
          <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.24.0:exe:${os.detected.classifier}</pluginArtifact>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>compile-custom</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

</project>

Générer les bouchons

Lorsque vous créez l'application, le plug-in convertit les définitions proto en code Java.

$ mvn -DskipTests package

Pour afficher les fichiers générés:

$ find target/generated-sources

Implémenter le service

Commencez par créer une classe GreetingServiceImpl qui implémentera l'opération greeting:

src/main/java/com/example/grpc/GreetingServiceImpl.java

package com.example.grpc;

import io.grpc.stub.StreamObserver;

public class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase {
  @Override
  public void greeting(GreetingServiceOuterClass.HelloRequest request,
        StreamObserver<GreetingServiceOuterClass.HelloResponse> responseObserver) {
  // HelloRequest has toString auto-generated.
    System.out.println(request);

    // You must use a builder to construct a new Protobuffer object
    GreetingServiceOuterClass.HelloResponse response = GreetingServiceOuterClass.HelloResponse.newBuilder()
      .setGreeting("Hello there, " + request.getName())
      .build();

    // Use responseObserver to send a single response back
    responseObserver.onNext(response);

    // When you are done, you must call onCompleted.
    responseObserver.onCompleted();
  }
}

Implémenter le serveur

Enfin, vous devez démarrer un serveur pour écouter un port et enregistrer cette implémentation de service. Modifiez la classe App et sa méthode principale:

src/main/java/com/example/grpc/App.java

package com.example.grpc;

import io.grpc.*;

public class App
{
    public static void main( String[] args ) throws Exception
    {
      // Create a new server to listen on port 8080
      Server server = ServerBuilder.forPort(8080)
        .addService(new GreetingServiceImpl())
        .build();

      // Start the server
      server.start();

      // Server threads are running in the background.
      System.out.println("Server started");
      // Don't exit the main thread. Wait until server is terminated.
      server.awaitTermination();
    }
}

Enfin, exécutez le serveur:

$ mvn -DskipTests package exec:java -Dexec.mainClass=com.example.grpc.App
...
Server Started

4. Utiliser le service

Le générateur a déjà généré toutes les bouchons côté client. Pour simplifier l'atelier, nous allons utiliser le même projet Maven, mais simplement ajouter une classe Client avec une nouvelle méthode principale.

Tout d'abord, cliquez sur + pour ouvrir une nouvelle session Cloud Shell afin de ne pas avoir à arrêter le serveur:

1ff0fda960b9adfc.png

Dans la nouvelle session, passez au répertoire grpc-hello-server:

$ cd grpc-hello-server

Ajoutez ensuite la nouvelle classe Client:

src/main/java/com/example/grpc/Client.java

package com.example.grpc;

import io.grpc.*;

public class Client
{
    public static void main( String[] args ) throws Exception
    {
      // Channel is the abstraction to connect to a service endpoint
      // Let's use plaintext communication because we don't have certs
      final ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:8080")
        .usePlaintext(true)
        .build();

      // It is up to the client to determine whether to block the call
      // Here we create a blocking stub, but an async stub,
      // or an async stub with Future are always possible.
      GreetingServiceGrpc.GreetingServiceBlockingStub stub = GreetingServiceGrpc.newBlockingStub(channel);
      GreetingServiceOuterClass.HelloRequest request =
        GreetingServiceOuterClass.HelloRequest.newBuilder()
          .setName("Ray")
          .build();

      // Finally, make the call using the stub
      GreetingServiceOuterClass.HelloResponse response = 
        stub.greeting(request);

      System.out.println(response);

      // A Channel should be shutdown before stopping the process.
      channel.shutdownNow();
    }
}

Enfin, exécutez le client:

$ mvn -DskipTests package exec:java -Dexec.mainClass=com.example.grpc.Client
...
greeting: "Hello there, Ray"

Et voilà ! Plutôt simple, non ?

5. Service de streaming

Vous pouvez encore essayer beaucoup d'autres fonctionnalités. Par exemple, vous pouvez créer facilement un service de streaming en ajoutant simplement le mot clé stream dans le fichier proto au paramètre de requête ou de réponse.

src/main/proto/GreetingService.proto

syntax = "proto3";
package com.example.grpc;

...

// Defining a Service, a Service can have multiple RPC operations
service GreetingService {
  // MODIFY HERE: Update the return to streaming return.
  rpc greeting(HelloRequest) returns (stream HelloResponse);
}

Mettez à jour votre serveur pour envoyer plusieurs réponses plutôt qu'une seule. Pour ce faire, effectuez plusieurs appels responseObserver.onNext(...):

src/main/java/com/example/grpc/GreetingServiceImpl.java

package com.example.grpc;

import io.grpc.stub.StreamObserver;

public class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase {
  @Override
  public void greeting(GreetingServiceOuterClass.HelloRequest request,
        StreamObserver<GreetingServiceOuterClass.HelloResponse> responseObserver) {
 
    ...

    // Feel free to construct different responses if you'd like.
    responseObserver.onNext(response);
    responseObserver.onNext(response);
    responseObserver.onNext(response);

    // When you are done, you must call onCompleted.
    responseObserver.onCompleted();
  }
}

Le client doit utiliser un bouchon asynchrone au lieu du bouchon bloquant. Mettez à jour le code client en veillant à remplacer le type stub par GreetingServiceStub:

src/main/java/com/example/grpc/Client.java

package com.example.grpc;

import io.grpc.*;

// New import
import io.grpc.stub.*;

public class Client
{
    public static void main( String[] args ) throws Exception
    {
      final ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:8080")
        .usePlaintext(true)
        .build();

      // Replace the previous synchronous code with asynchronous code.
      // This time use an async stub:
       GreetingServiceGrpc.GreetingServiceStub stub = GreetingServiceGrpc.newStub(channel);

      // Construct a request
      GreetingServiceOuterClass.HelloRequest request =
        GreetingServiceOuterClass.HelloRequest.newBuilder()
          .setName("Ray")
          .build();

      // Make an Asynchronous call. Listen to responses w/ StreamObserver
      stub.greeting(request, new StreamObserver<GreetingServiceOuterClass.HelloResponse>() {
        public void onNext(GreetingServiceOuterClass.HelloResponse response) {
          System.out.println(response);
        }
        public void onError(Throwable t) {
        }
        public void onCompleted() {
          // Typically you'll shutdown the channel somewhere else.
          // But for the purpose of the lab, we are only making a single
          // request. We'll shutdown as soon as this request is done.
          channel.shutdownNow();
        }
      });
    }
}

Recompilez l'application:

$ mvn -DskipTests package

Redémarrez le serveur et le client dans leur propre session Cloud Shell.

Pour démarrer le serveur:

$ mvn exec:java -Dexec.mainClass=com.example.grpc.App
...
Server Started

Pour démarrer le client:

$ mvn exec:java -Dexec.mainClass=com.example.grpc.Client
...
greeting: "Hello there, Ray"
greeting: "Hello there, Ray"
greeting: "Hello there, Ray"

6. Félicitations !

Points abordés :

  • Le langage de tampon de protocole
  • Mettre en œuvre un serveur gRPC à l'aide de Java
  • Implémenter un client gRPC à l'aide de Java

Prochaines étapes :

Votre avis nous intéresse !

  • Veuillez prendre quelques minutes pour répondre à notre courte enquête.