gRPC-Dienst mit Java erstellen

1. Übersicht

gRPC ist ein sprach- und plattformneutrales RPC-Framework und -Toolset, das bei Google entwickelt wurde. Sie können damit einen Dienst mithilfe von Protocol Buffers definieren, einem besonders leistungsstarken Toolset und einer Sprache für die binäre Serialisierung. Anschließend können Sie idiomatische Client- und Server-Stubs aus Ihrer Dienstdefinition in verschiedenen Sprachen generieren.

In diesem Codelab erfahren Sie, wie Sie einen Java-basierten Dienst erstellen, der eine API mithilfe des gRPC-Frameworks freigibt, und dann einen Client schreiben, der den generierten clientseitigen gRPC-Stub verwendet.

Aufgaben in diesem Lab

  • Die Protokollzwischenspeichersprache
  • gRPC-Dienst mit Java implementieren
  • gRPC-Client mit Java implementieren

Wie möchten Sie diese Anleitung nutzen?

<ph type="x-smartling-placeholder"></ph> Nur bis zum Ende lesen Lies sie dir durch und absolviere die Übungen

Wie würden Sie Ihre Erfahrung mit der Erstellung von Node.js-Anwendungen bewerten?

<ph type="x-smartling-placeholder"></ph> Neuling Mittel Kompetent

Wie würden Sie Ihre Erfahrung mit der Erstellung von Go-Apps bewerten?

<ph type="x-smartling-placeholder"></ph> Neuling Mittel Kompetent

2. Einrichtung und Anforderungen

Umgebung für das selbstbestimmte Lernen einrichten

  1. Melden Sie sich in der Cloud Console an und erstellen Sie ein neues Projekt oder verwenden Sie ein vorhandenes Projekt. Wenn Sie noch kein Gmail- oder Google Workspace-Konto haben, müssen Sie eines erstellen.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

Notieren Sie sich die Projekt-ID, also den projektübergreifend nur einmal vorkommenden Namen eines Google Cloud-Projekts. Der oben angegebene Name ist bereits vergeben und kann leider nicht mehr verwendet werden. Sie wird in diesem Codelab später als PROJECT_ID bezeichnet.

  1. Als Nächstes müssen Sie in der Cloud Console die Abrechnung aktivieren, um Google Cloud-Ressourcen nutzen zu können.

Dieses Codelab sollte möglichst wenig kosten. Folgen Sie der Anleitung im Abschnitt „Bereinigen“, . Hier erfahren Sie, wie Sie Ressourcen herunterfahren, damit Ihnen über dieses Tutorial hinaus keine Kosten entstehen. Neue Google Cloud-Nutzer haben Anspruch auf eine kostenlose Testversion mit 300$Guthaben.

Google Cloud Shell

Sie können dieses Codelab zwar auf Ihrem Computer ausführen, im Codelab verwenden wir jedoch Google Cloud Shell, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.

Diese Debian-basierte virtuelle Maschine verfügt über alle erforderlichen Entwicklungstools. Es bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und wird in Google Cloud ausgeführt. Dadurch werden die Netzwerkleistung und die Authentifizierung erheblich verbessert. Für dieses Codelab benötigen Sie also nur einen Browser – ja, er funktioniert auf Chromebooks.

  1. Klicken Sie einfach auf Cloud Shell aktivieren a8460e837e9f5fda.png, um Cloud Shell über die Cloud Console zu aktivieren. Die Bereitstellung und Verbindung mit der Umgebung dauert einen Moment.

b532b2f19ab85dda.png

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

Sobald Sie mit Cloud Shell verbunden sind, sollten Sie sehen, dass Sie bereits authentifiziert sind und dass das Projekt bereits auf Ihre PROJECT_ID eingestellt ist.

gcloud auth list

Befehlsausgabe

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

Befehlsausgabe

[core]
project = <PROJECT_ID>

Sollte das Projekt aus irgendeinem Grund nicht eingerichtet sein, geben Sie einfach den folgenden Befehl ein:

gcloud config set project <PROJECT_ID>

Du suchst dein Gerät (PROJECT_ID)? Sehen Sie nach, welche ID Sie bei den Einrichtungsschritten verwendet haben, oder rufen Sie sie im Dashboard der Cloud Console auf:

2485e00c1223af09.png

Cloud Shell legt außerdem standardmäßig einige Umgebungsvariablen fest, die bei der Ausführung zukünftiger Befehle nützlich sein können.

echo $GOOGLE_CLOUD_PROJECT

Befehlsausgabe

<PROJECT_ID>
  1. Legen Sie schließlich die Standardzone und die Projektkonfiguration fest.
gcloud config set compute/zone us-central1-f

Sie können verschiedene Zonen auswählen. Weitere Informationen finden Sie unter Regionen und Zonen.

3. gRPC-Dienst erstellen

Erstellen Sie ein neues Java-Projekt mit Maven:

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

gRPC-Definitionsdatei hinzufügen

In gRPC müssen Dienstnutzlasten (Anfrage und Antwort) und die Dienstvorgänge in einer IDL (Interface Definition Language) erfasst werden. gRPC verwendet die Protobuffer 3-Syntax, um Nachrichtennutzlasten und -vorgänge zu definieren. Lassen Sie uns eine Proto-Datei für einen einfachen Begrüßungsdienst mit einer Hello-Anfrage und einer Hello-Antwort erstellen.

Erstellen Sie zunächst ein neues Proto-Verzeichnis für die neue Proto-Datei:

$ mkdir -p src/main/proto

Erstellen Sie dann eine neue Proto-Datei src/main/proto/GreetingService.proto.

Sie können vim,nano, oder emacs verwenden, um die Datei zu bearbeiten:

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

gRPC-Abhängigkeiten und Plug-in hinzufügen

Sobald Sie über die Definition verfügen, können wir sowohl den serverseitigen Stub als auch den clientseitigen Stub aus dieser Datei generieren. Sie müssen die gRPC-Abhängigkeiten und -Plug-ins hinzufügen.

Fügen Sie zuerst die gRPC-Abhängigkeiten zu pom.xml hinzu:

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>

Fügen Sie dann das Plug-in hinzu:

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>

Stubs generieren

Wenn Sie die Anwendung erstellen, konvertiert das Plug-in die Proto-Definitionen in Java-Code.

$ mvn -DskipTests package

So rufen Sie die generierten Dateien auf:

$ find target/generated-sources

Dienst implementieren

Erstellen Sie zuerst eine neue GreetingServiceImpl-Klasse, die den greeting-Vorgang implementiert:

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

Server implementieren

Abschließend müssen Sie einen Server starten, der einen Port überwacht und diese Dienstimplementierung registriert. Bearbeiten Sie die App-Klasse und ihre Hauptmethode:

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

Abschließend führen Sie den Server aus:

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

4. Nutzung des Dienstes

Der Generator hat bereits alle clientseitigen Stubs generiert. Der Einfachheit halber verwenden wir dasselbe Maven-Projekt, fügen aber einfach eine neue Client-Klasse mit einer neuen Main-Methode hinzu.

Klicken Sie zuerst auf +, um eine neue Cloud Shell-Sitzung zu öffnen, damit Sie den Server nicht beenden müssen:

1ff0fda960b9adfc.png

Wechseln Sie in der neuen Sitzung zum Verzeichnis grpc-hello-server:

$ cd grpc-hello-server

Fügen Sie dann die neue Klasse Client hinzu:

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

Führen Sie zum Schluss den Client aus:

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

Fertig! Ganz einfach, oder?

5. Streamingdienst

Es gibt noch viel mehr, das Sie ausprobieren können. Sie können beispielsweise ganz einfach einen Streamingdienst erstellen, indem Sie einfach das Schlüsselwort stream in der .proto-Datei entweder dem Anfrage- oder dem Antwortparameter hinzufügen, z. B.

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

Aktualisieren Sie Ihren Server so, dass nicht nur eine, sondern mehrere Antworten gesendet werden. Führen Sie dazu mehrere responseObserver.onNext(...)-Aufrufe durch:

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

Der Client muss einen asynchronen Stub anstelle des blockierenden Stubs verwenden. Aktualisieren Sie den Clientcode und ändern Sie den Typ 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();
        }
      });
    }
}

Erstellen Sie die Anwendung neu:

$ mvn -DskipTests package

Starten Sie den Server und den Client jeweils in einer eigenen Cloud Shell-Sitzung neu.

So starten Sie den Server:

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

So starten Sie den Client:

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

6. Glückwunsch!

Behandelte Themen:

  • Die Protokollzwischenspeichersprache
  • gRPC-Server mit Java implementieren
  • gRPC-Client mit Java implementieren

Vorgehensweise:

Feedback geben

  • Bitte nehmen Sie sich einen Moment Zeit, um an unserer kurzen Umfrage teilzunehmen.