Tworzenie usługi gRPC za pomocą Javy

1. Omówienie

gRPC to neutralna dla języka platforma i zbiór narzędzi opracowane przez Google do zdalnego wywoływania procedur (RPC). Umożliwia definiowanie usługi za pomocą buforów protokołów, czyli szczególnie zaawansowanych narzędzi i języka do serializacji plików binarnych. Następnie umożliwia generowanie idiomatycznych skrótów klientów i serwerów z definicji usługi w różnych językach.

Z tego ćwiczenia w programie dowiesz się, jak za pomocą platformy gRPC utworzyć usługę opartą na Javie, która udostępnia interfejs API, oraz jak napisać klienta, który będzie używał wygenerowanego fragmentu kodu gRPC po stronie klienta.

Czego się nauczysz

  • język bufora protokołu
  • Jak wdrożyć usługę gRPC za pomocą Javy
  • Jak wdrożyć klienta gRPC za pomocą Javy

Jak wykorzystasz ten samouczek?

Tylko do przeczytania Przeczytaj go i wykonaj ćwiczenia

Jak oceniasz swoje doświadczenia z tworzeniem aplikacji Node.js?

Początkujący Poziom średnio zaawansowany Biegły

Jak oceniasz swoje doświadczenia z tworzeniem aplikacji w języku Go?

Początkujący Poziom średnio zaawansowany Biegły
.

2. Konfiguracja i wymagania

Samodzielne konfigurowanie środowiska

  1. Zaloguj się w konsoli Google Cloud i utwórz nowy projekt lub wykorzystaj już istniejący. Jeśli nie masz jeszcze konta Gmail ani Google Workspace, musisz je utworzyć.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

Zapamiętaj identyfikator projektu, unikalną nazwę we wszystkich projektach Google Cloud (powyższa nazwa jest już zajęta i nie będzie Ci odpowiadać). W dalszej części tego ćwiczenia w Codelabs będzie ona określana jako PROJECT_ID.

  1. Następnie musisz włączyć płatności w Cloud Console, aby korzystać z zasobów Google Cloud.

Ukończenie tego ćwiczenia z programowania nie powinno kosztować zbyt wiele. Postępuj zgodnie z instrukcjami podanymi w sekcji „Czyszczenie” W tym samouczku znajdziesz wskazówki, jak wyłączyć zasoby, aby uniknąć naliczania opłat. Nowi użytkownicy Google Cloud mogą skorzystać z programu bezpłatnego okresu próbnego o wartości 300 USD.

Google Cloud Shell,

Choć możesz wykonywać te ćwiczenia z programowania z poziomu Twojego komputera, w ramach tego ćwiczenia użyjemy Google Cloud Shell – środowiska wiersza poleceń działającego w Cloud.

Ta maszyna wirtualna oparta na Debianie zawiera wszystkie potrzebne narzędzia dla programistów. Zawiera stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i uwierzytelnianie. Oznacza to, że do tego ćwiczenia z programowania wystarczy przeglądarka (tak, działa ona na Chromebooku).

  1. Aby aktywować Cloud Shell z poziomu konsoli Cloud, kliknij Aktywuj Cloud Shell a8460e837e9f5fda.png (udostępnienie środowiska i połączenie z nim powinno zająć tylko chwilę).

b532b2f19ab85dda.png

Zrzut ekranu 2017-06-14 o 10.13.43 PM.png

Po nawiązaniu połączenia z Cloud Shell powinno pojawić się potwierdzenie, że użytkownik jest już uwierzytelniony, a projekt jest już ustawiony na PROJECT_ID.

gcloud auth list

Dane wyjściowe polecenia

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

Dane wyjściowe polecenia

[core]
project = <PROJECT_ID>

Jeśli z jakiegoś powodu projekt nie jest skonfigurowany, uruchom po prostu to polecenie:

gcloud config set project <PROJECT_ID>

Szukasz urządzenia PROJECT_ID? Sprawdź identyfikator użyty w krokach konfiguracji lub wyszukaj go w panelu Cloud Console:

2485e00c1223af09.png

Cloud Shell ustawia też domyślnie niektóre zmienne środowiskowe, które mogą być przydatne podczas uruchamiania kolejnych poleceń.

echo $GOOGLE_CLOUD_PROJECT

Dane wyjściowe polecenia

<PROJECT_ID>
  1. Na koniec ustaw domyślną strefę i konfigurację projektu.
gcloud config set compute/zone us-central1-f

Możesz wybrać różne strefy. Więcej informacji znajdziesz w artykule Regiony i Strefy.

3. Tworzenie usługi gRPC

Utwórz nowy projekt Java w narzędziu Maven:

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

Dodaj plik definicji gRPC

W gRPC ładunki usługi (żądania i odpowiedzi) oraz operacje związane z usługą muszą być przechwytywane w języku IDL (Interface Definition Language). Do definiowania ładunków wiadomości i operacji w gRPC używa składni Protobuffer 3. Utwórzmy plik proto dla prostej usługi powitania z odpowiedziami Hello Request i Hello.

Najpierw utwórz nowy katalog proto, w którym będzie znajdować się nowy plik proto:

$ mkdir -p src/main/proto

Następnie utwórz nowy plik proto src/main/proto/GreetingService.proto.

Aby edytować plik, możesz użyć vim,nano, lub emacs:

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

Dodaj zależności gRPC i wtyczkę

Gdy będziesz już mieć definicję, będziemy mogli wygenerować z tego pliku wycinek po stronie serwera i kod po stronie klienta. Musisz dodać zależności i wtyczki gRPC.

Najpierw dodaj zależności gRPC do interfejsu 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>

Następnie dodaj wtyczkę:

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>

Wygeneruj częśćty

Gdy skompilujesz aplikację, wtyczka przekonwertuje definicje proto na kod w Javie.

$ mvn -DskipTests package

Aby wyświetlić wygenerowane pliki:

$ find target/generated-sources

Wdrażanie Usługi

Najpierw utwórz nową klasę GreetingServiceImpl, która wdroży operację 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();
  }
}

Wdrażanie serwera

Na koniec musisz uruchomić serwer, aby nasłuchiwać na porcie i zarejestrować tę implementację usługi. Edytuj klasę App i jej główną metodę:

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

Na koniec uruchom serwer:

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

4. Korzystanie z Usługi

Generator wygenerował już wszystkie atrapy po stronie klienta. Aby ułatwić korzystanie z modułu, użyjemy tego samego projektu Maven, ale po prostu dodamy nową klasę Client z nową metodą główną.

Najpierw kliknij +, aby otworzyć nową sesję Cloud Shell, dzięki której nie będzie trzeba zamykać serwera:

1ff0fda960b9adfc.png

W nowej sesji przejdź do katalogu grpc-hello-server:

$ cd grpc-hello-server

Następnie dodaj nową klasę 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();
    }
}

Na koniec uruchom klienta:

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

Znakomicie. To całkiem proste, prawda?

5. Usługa strumieniowania

Możesz spróbować znacznie więcej. Aby na przykład łatwo utworzyć usługę strumieniowania, po prostu dodaj słowo kluczowe stream w proto pliku do żądania lub odpowiedzi, np.

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

Zaktualizuj serwer, aby wysyłał wiele odpowiedzi, a nie tylko jedną. Możesz to zrobić, nawiązując kilka wywołań funkcji 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();
  }
}

Klient musi używać fragmentu kodu asynchronicznego zamiast fragmentu blokującego. Zaktualizuj kod klienta, zmieniając typ stub na 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();
        }
      });
    }
}

Ponownie skompiluj aplikację:

$ mvn -DskipTests package

Uruchom ponownie serwer i klienta w osobnej sesji Cloud Shell.

Aby uruchomić serwer:

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

Aby uruchomić klienta:

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

6. Gratulacje!

Omówione zagadnienia:

  • język bufora protokołu
  • Jak wdrożyć serwer gRPC za pomocą Javy
  • Jak wdrożyć klienta gRPC za pomocą Javy

Dalsze kroki:

Prześlij nam swoją opinię

  • Poświęć chwilę na wypełnienie naszej bardzo krótkiej ankiety