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

1. Обзор

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

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

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

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

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

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

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

Новичок Средний Опытный

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

Новичок Средний Опытный

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

Самостоятельная настройка среды

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

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

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

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

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

Google Cloud Shell

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

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

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

b532b2f19ab85dda.png

Снимок экрана 14.06.2017, 22.13.43.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 ? Узнайте, какой идентификатор вы использовали на этапах настройки, или найдите его на панели управления 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 для хранения нового файла proto:

$ mkdir -p src/main/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 и его основной метод:

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 с новым основным методом.

Сначала нажмите + , чтобы открыть новый сеанс 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 в файле прототипа либо к параметру запроса, либо к параметру ответа, например

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

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

Оставьте нам свой отзыв

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