Создание службы gRPC с помощью Java

1. Обзор

gRPC — это независимая от языка программирования и платформы платформа удаленного вызова процедур (RPC) и набор инструментов, разработанные в Google. Она позволяет определять сервис с помощью Protocol Buffers, особенно мощного набора инструментов и языка для бинарной сериализации. Затем она позволяет генерировать идиоматические клиентские и серверные заглушки из определения вашего сервиса на различных языках.

В этом практическом занятии вы научитесь создавать Java-сервис, предоставляющий API с использованием фреймворка gRPC, а затем писать клиент для использования сгенерированного клиентского заглушки gRPC.

Что вы узнаете

  • Язык протокола буферизации
  • Как реализовать gRPC-сервис с использованием Java
  • Как реализовать gRPC-клиент с использованием Java

Как вы будете использовать этот учебник?

Прочитайте только от начала до конца. Прочитайте текст и выполните упражнения.

Как бы вы оценили свой опыт разработки приложений на Node.js?

Новичок Средний Профессионал

Как бы вы оценили свой опыт разработки приложений на Go?

Новичок Средний Профессионал

2. Настройка и требования

Настройка среды для самостоятельного обучения

  1. Войдите в Cloud Console и создайте новый проект или используйте существующий. Если у вас еще нет учетной записи Gmail или Google Workspace, вам необходимо ее создать .

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

Запомните идентификатор проекта (Project ID) — уникальное имя для всех проектов Google Cloud (указанное выше имя уже занято и вам не подойдёт, извините!). В дальнейшем в этом практическом занятии оно будет обозначаться как PROJECT_ID .

  1. Далее вам потребуется включить оплату в Cloud Console, чтобы использовать ресурсы Google Cloud.

Выполнение этого практического задания не должно стоить дорого, если вообще что-либо. Обязательно следуйте инструкциям в разделе «Очистка», где указано, как отключить ресурсы, чтобы избежать дополнительных расходов после завершения этого урока. Новые пользователи Google Cloud имеют право на бесплатную пробную версию стоимостью 300 долларов США .

Google Cloud Shell

Хотя эту практическую работу можно выполнить с вашего компьютера, в данном случае мы будем использовать Google Cloud Shell — среду командной строки, работающую в облаке.

Эта виртуальная машина на базе Debian содержит все необходимые инструменты разработки. Она предоставляет постоянный домашний каталог размером 5 ГБ и работает в облаке Google, что значительно повышает производительность сети и аутентификацию. Это означает, что для выполнения этого практического задания вам понадобится только браузер (да, он работает и на Chromebook).

  1. Для активации Cloud Shell из консоли Cloud Console просто нажмите «Активировать Cloud Shell». a8460e837e9f5fda.png (На подготовку и подключение к среде должно уйти всего несколько минут).

b532b2f19ab85dda.png

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

После подключения к Cloud Shell вы увидите, что ваша аутентификация пройдена и проект уже настроен на ваш PROJECT_ID .

gcloud auth list

вывод команды

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

вывод команды

[core]
project = <PROJECT_ID>

Если по какой-либо причине проект не создан, просто выполните следующую команду:

gcloud config set project <PROJECT_ID>

Ищете свой PROJECT_ID ? Проверьте, какой ID вы использовали на этапах настройки, или найдите его на панели управления Cloud Console:

2485e00c1223af09.png

Cloud Shell также по умолчанию устанавливает некоторые переменные среды, которые могут быть полезны при выполнении будущих команд.

echo $GOOGLE_CLOUD_PROJECT

вывод команды

<PROJECT_ID>
  1. Наконец, установите зону по умолчанию и конфигурацию проекта.
gcloud config set compute/zone us-central1-f

Вы можете выбрать различные зоны. Для получения дополнительной информации см. раздел «Регионы и зоны» .

3. Создайте службу gRPC.

Создайте новый Java-проект с помощью Maven:

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

Добавить файл определения gRPC

В gRPC полезная нагрузка сервиса (запрос и ответ) и операции сервиса должны быть описаны в IDL (языке определения интерфейса). gRPC использует синтаксис Protobuffer 3 для определения полезной нагрузки сообщений и операций. Давайте создадим proto-файл для простого сервиса приветствия с запросом Hello и ответом Hello.

Сначала создайте новую директорию proto для хранения нового файла proto:

$ mkdir -p src/main/proto

Затем создайте новый proto-файл src/main/proto/GreetingService.proto .

Для редактирования файла можно использовать vim,nano, или 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);
}

Добавьте зависимости gRPC и плагин.

После того, как у вас будет определение, мы сможем сгенерировать из этого файла как серверный, так и клиентский заглушки. Вам потребуется добавить зависимости и плагины gRPC.

Сначала добавьте зависимости gRPC в файл 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>

Затем добавьте плагин:

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>

Сгенерировать заглушки

При сборке приложения плагин преобразует определения протоколов в код Java.

$ mvn -DskipTests package

Чтобы посмотреть сгенерированные файлы:

$ find target/generated-sources

Внедрить сервис

Сначала создайте новый класс GreetingServiceImpl , который будет реализовывать операцию 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();
  }
}

Реализация сервера

Наконец, вам потребуется запустить сервер для прослушивания порта и зарегистрировать реализацию этого сервиса. Отредактируйте класс App и его метод main:

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

Наконец, запустите сервер:

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

4. Использование услуги

Генератор уже сгенерировал все клиентские заглушки. Для упрощения лабораторной работы мы будем использовать тот же проект Maven, но просто добавим новый класс Client с новым методом main.

Сначала нажмите на кнопку «+» , чтобы открыть новую сессию Cloud Shell и не завершать работу сервера:

1ff0fda960b9adfc.png

В новой сессии перейдите в каталог grpc-hello-server :

$ cd grpc-hello-server

Затем добавьте новый класс 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();
    }
}

Наконец, запустите клиент:

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

Вот и всё! Довольно просто, правда?

5. Стриминговый сервис

Есть еще много чего, что вы можете попробовать. Например, вы можете легко создать потоковый сервис, просто добавив ключевое слово stream в proto-файл либо к параметру запроса, либо к параметру ответа, например.

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

Обновите свой сервер, чтобы он отправлял несколько ответов вместо одного. Это можно сделать, выполнив несколько вызовов 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();
  }
}

Клиент должен использовать асинхронный заглушечный модуль вместо блокирующего. Обновите код клиента, убедившись, что тип stub изменен на 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();
        }
      });
    }
}

Пересоберите приложение:

$ mvn -DskipTests package

Перезапустите сервер и клиент, каждый в отдельной сессии Cloud Shell.

Для запуска сервера:

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

Для запуска клиента:

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

6. Поздравляем!

Что мы рассмотрели:

  • Язык протокола буферизации
  • Как реализовать gRPC-сервер с использованием Java
  • Как реализовать gRPC-клиент с использованием Java

Следующие шаги:

Оставьте свой отзыв.

  • Пожалуйста, уделите несколько минут, чтобы пройти наш очень короткий опрос.