Creazione di un servizio gRPC con Java

1. Panoramica

gRPC è un framework e un set di strumenti per le chiamate di procedure remote (RPC) indipendenti dal linguaggio e dalla piattaforma sviluppati da Google. Consente di definire un servizio mediante buffer di protocollo, un set di strumenti e un linguaggio di serializzazione binaria particolarmente potente. Consente quindi di generare stub client e server idiomatici dalla definizione del tuo servizio in varie lingue.

In questo codelab, imparerai a creare un servizio basato su Java che espone un'API utilizzando il framework gRPC, quindi a scrivere un client per utilizzare lo stub lato client gRPC generato.

Cosa imparerai a fare

  • Protocol Buffer Language (Linguaggio del buffer di protocollo)
  • Come implementare un servizio gRPC utilizzando Java
  • Come implementare un client gRPC utilizzando Java

Come utilizzerai questo tutorial?

Solo lettura Leggilo e completa gli esercizi

Come valuteresti la tua esperienza con la creazione di app Node.js?

Principiante Livello intermedio Eccellente

Come giudichi la tua esperienza con la creazione di app Go?

Principiante Livello intermedio Eccellente

2. Configurazione e requisiti

Configurazione dell'ambiente da seguire in modo autonomo

  1. Accedi alla console Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai ancora un account Gmail o Google Workspace, devi crearne uno.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

Ricorda l'ID progetto, un nome univoco in tutti i progetti Google Cloud (il nome precedente è già stato utilizzato e non funzionerà correttamente). Verrà indicato più avanti in questo codelab come PROJECT_ID.

  1. Successivamente, dovrai abilitare la fatturazione in Cloud Console per utilizzare le risorse Google Cloud.

Eseguire questo codelab non dovrebbe costare molto. Assicurati di seguire le istruzioni nella sezione "Pulizia" in cui viene spiegato come arrestare le risorse in modo da non incorrere in fatturazione oltre questo tutorial. I nuovi utenti di Google Cloud sono idonei al programma prova senza costi di 300$.

Google Cloud Shell

Anche se questo codelab può essere gestito dal tuo computer, in questo codelab utilizzeremo Google Cloud Shell, un ambiente a riga di comando in esecuzione nel cloud.

Questa macchina virtuale basata su Debian viene caricata con tutti gli strumenti di sviluppo necessari. Offre una home directory permanente da 5 GB e viene eseguita in Google Cloud, migliorando notevolmente le prestazioni di rete e l'autenticazione. Ciò significa che per questo codelab è sufficiente un browser (sì, funziona su Chromebook).

  1. Per attivare Cloud Shell dalla console Cloud, fai semplicemente clic su Attiva Cloud Shell a8460e837e9f5fda.png (il provisioning e la connessione all'ambiente dovrebbero richiedere solo pochi minuti).

b532b2f19ab85dda.png

Screen Shot 2017-06-14 at 10.13.43 PM.png

Dopo la connessione a Cloud Shell, dovresti vedere che hai già eseguito l'autenticazione e che il progetto è già impostato su PROJECT_ID.

gcloud auth list

Output comando

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

Output comando

[core]
project = <PROJECT_ID>

Se, per qualche motivo, il progetto non è impostato, invia semplicemente il seguente comando:

gcloud config set project <PROJECT_ID>

Stai cercando il tuo PROJECT_ID? Controlla l'ID utilizzato nei passaggi di configurazione o cercalo nella dashboard della console Cloud:

2485e00c1223af09.png

Cloud Shell imposta anche alcune variabili di ambiente per impostazione predefinita, cosa che può essere utile quando eseguirai comandi futuri.

echo $GOOGLE_CLOUD_PROJECT

Output comando

<PROJECT_ID>
  1. Infine, imposta la zona e la configurazione del progetto predefinite.
gcloud config set compute/zone us-central1-f

Puoi scegliere zone diverse. Per ulteriori informazioni, consulta Regioni e Zone.

3. Crea un servizio gRPC

Crea un nuovo progetto Java con Maven:

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

Aggiungi un file di definizione gRPC

In gRPC, i payload dei servizi (richiesta e risposta) e le operazioni di servizio devono essere acquisiti in un IDL (Interface Definition Language). gRPC utilizza la sintassi Protobuffer 3 per definire i payload e le operazioni dei messaggi. Creiamo un file proto per un semplice servizio di benvenuto con una richiesta Hello e una risposta Hello.

Per prima cosa, crea una nuova directory proto in cui conservare il nuovo file proto:

$ mkdir -p src/main/proto

Quindi, crea un nuovo file di protocollo src/main/proto/GreetingService.proto.

Puoi utilizzare vim,nano, o emacs per modificare il file:

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

Aggiungi dipendenze gRPC e plug-in

Dopo aver ottenuto la definizione, da questo file possiamo generare sia lo stub lato server che lo stub lato client. Dovrai aggiungere le dipendenze e i plug-in gRPC.

Innanzitutto, aggiungi le dipendenze gRPC a 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>

Quindi, aggiungi il 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>

Generare Stub

Quando crei l'applicazione, il plug-in converte le definizioni del protocollo in codice Java.

$ mvn -DskipTests package

Per visualizzare i file generati:

$ find target/generated-sources

Implementare il servizio

Innanzitutto, crea una nuova classe GreetingServiceImpl che implementerà l'operazione 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();
  }
}

Implementa il server

Infine, dovrai avviare un server per eseguire l'ascolto su una porta e registrare l'implementazione del servizio. Modifica la classe App e il relativo metodo 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();
    }
}

Infine, esegui il server:

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

4. Utilizzo del servizio

Il generatore ha già generato tutti gli stub lato client. Per semplicità del lab, utilizzeremo lo stesso progetto Maven, ma semplicemente aggiungeremo una nuova classe Client con un nuovo metodo principale.

Innanzitutto, fai clic su + per aprire una nuova sessione di Cloud Shell in modo da non dover terminare il server:

1ff0fda960b9adfc.png

Nella nuova sessione, passa alla directory grpc-hello-server:

$ cd grpc-hello-server

Quindi, aggiungi la nuova 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();
    }
}

Infine, esegui il client:

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

È tutto. È un gioco da ragazzi, no?

5. Servizio di streaming

Puoi provare molto di più. Ad esempio, puoi creare facilmente un servizio di streaming semplicemente aggiungendo la parola chiave stream nel file proto al parametro di richiesta o di risposta, ad es.

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

Aggiorna il server in modo che invii più risposte anziché una sola. Puoi farlo effettuando più chiamate a 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();
  }
}

Il client deve utilizzare uno stub asincrono al posto dello stub di blocco. Aggiorna il codice client assicurandoti di aggiornare il tipo stub in 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();
        }
      });
    }
}

Ricrea l'applicazione:

$ mvn -DskipTests package

Riavvia sia il server che il client, ciascuno nella propria sessione di Cloud Shell.

Per avviare il server:

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

Per avviare il client:

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

6. Complimenti!

Argomenti trattati:

  • Protocol Buffer Language (Linguaggio del buffer di protocollo)
  • Come implementare un server gRPC utilizzando Java
  • Come implementare un client gRPC utilizzando Java

Passaggi successivi:

Inviaci il tuo feedback

  • Dedica qualche istante a completare il nostro breve sondaggio