Введение в gRPC

Введение в gRPC

1. Вступление

gRPC is a high performance, open source RPC framework initially developed by Google. Это помогает избавиться от шаблонного кода и помогает подключать полиглот-сервисы в центрах обработки данных и между ними.

2. обзор

Структура основана на клиент-серверной модели удаленных вызовов процедур. A client application can directly call methods on a server application as if it was a local object.с

В этой статье будут использованы следующие шаги для создания типичного клиент-серверного приложения с использованием gRPC:

  1. Определите службу в файле.proto

  2. Генерация кода сервера и клиента с использованием компилятора буфера протокола

  3. Создайте серверное приложение, внедрив сгенерированные сервисные интерфейсы и породив сервер gRPC.

  4. Создайте клиентское приложение, делая вызовы RPC, используя сгенерированные заглушки

Давайте определим простойHelloService, который возвращает приветствия в обмен на имя и фамилию.

3. Maven Зависимости

Добавим зависимостиgrpc-netty,grpc-protobuf иgrpc-stub:


    io.grpc
    grpc-netty
    1.16.1


    io.grpc
    grpc-protobuf
    1.16.1


    io.grpc
    grpc-stub
    1.16.1

4. Определение службы

Начнем с определения службыspecifying methods that can be called remotely along with their parameters and return types.

Это делается в файле.proto с помощьюprotocol buffers. Они также используются для описания структуры сообщений полезной нагрузки.

4.1. Основные конфигурации

Давайте создадим файлHelloService.proto для нашего образцаHelloService. Мы начнем с добавления нескольких основных деталей конфигурации:

syntax = "proto3";
option java_multiple_files = true;
package org.example.grpc;

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

Наконец, мы указываем пакет, который мы хотим использовать для наших сгенерированных классов Java.

4.2. Определение структуры сообщения

Далее мы определяем сообщение:

message HelloRequest {
    string firstName = 1;
    string lastName = 2;
}

Это определяет полезную нагрузку запроса. Здесь каждый атрибут, который входит в сообщение, определяется вместе с его типом.

Каждому атрибуту должен быть присвоен уникальный номер, называемый тегом. This tag is used by the protocol buffer to represent the attribute instead of using the attribute name.с

Таким образом, в отличие от JSON, где мы передаем имя атрибутаfirstName каждый раз, буфер протокола будет использовать число 1 для представленияfirstName. Определение полезной нагрузки ответа аналогично запросу.

Обратите внимание, что мы можем использовать один и тот же тег для нескольких типов сообщений:

message HelloResponse {
    string greeting = 1;
}

4.3. Определение контракта на обслуживание

Наконец, давайте определимся с контрактом на обслуживание. Для нашегоHelloService мы определяем операциюhello():

service HelloService {
    rpc hello(HelloRequest) returns (HelloResponse);
}

Операцияhello() принимает унарный запрос и возвращает унарный ответ. gRPC также поддерживает потоковую передачу, добавляя ключевое словоstream к запросу и ответу.

5. Генерация кода

Теперь мы передаем файлHelloService.proto компилятору буфера протоколаprotoc для генерации файлов Java. Есть несколько способов вызвать это.

5.1. Использование компилятора буфера протокола

Download the compiler и следуйте инструкциям в файле README.

Вы можете использовать следующую команду для генерации кода:

protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/HelloService.proto

5.2. Использование плагина Maven

Как разработчик, вы бы хотели, чтобы процесс генерации кода был тесно интегрирован с вашей системой сборки. gRPC предоставляетprotobuf-maven-plugin для системы сборки Maven:


  
    
      kr.motd.maven
      os-maven-plugin
      1.6.1
    
  
  
    
      org.xolstice.maven.plugins
      protobuf-maven-plugin
      0.6.1
      
        
          com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}
        
        grpc-java
        
          io.grpc:protoc-gen-grpc-java:1.4.0:exe:${os.detected.classifier}
        
      
      
        
          
            compile
            compile-custom
          
        
      
    
  

Расширение / плагинos-maven-plugin генерирует различные полезные свойства проекта, зависящие от платформы, такие как$\{os.detected.classifier}

6. Создание сервера

Независимо от того, какой метод вы используете для генерации кода, будут сгенерированы следующие файлы ключей:

  • HelloRequest.java – содержит определение типаHelloRequest

  • HelloResponse.java *–* содержит определение типаHelleResponse

  • HelloServiceImplBase.java *–* содержит абстрактный классHelloServiceImplBase, который обеспечивает реализацию всех операций, которые мы определили в интерфейсе службы.

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

default implementation of the abstract class HelloServiceImplBase is to throw runtime exceptionio.grpc.StatusRuntimeException говорит, что метод не реализован.

Мы расширим этот класс и переопределим методhello(), упомянутый в определении нашей службы:

public class HelloServiceImpl extends HelloServiceImplBase {

    @Override
    public void hello(
      HelloRequest request, StreamObserver responseObserver) {

        String greeting = new StringBuilder()
          .append("Hello, ")
          .append(request.getFirstName())
          .append(" ")
          .append(request.getLastName())
          .toString();

        HelloResponse response = HelloResponse.newBuilder()
          .setGreeting(greeting)
          .build();

        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

Если мы сравним подписьhello() с той, которую мы записали в файлеHellService.proto, мы заметим, что она не возвращаетHelloResponse. Вместо этого он принимает второй аргумент какStreamObserver<HelloResponse>, который является наблюдателем ответа, обратным вызовом для сервера для вызова с его ответом.

Таким образомthe client gets an option to make a blocking call or a non-blocking call.

gRPC использует компоновщики для создания объектов. Мы используемHelloResponse.newBuilder() и устанавливаем текст приветствия для создания объектаHelloResponse. Мы устанавливаем этот объект в методonNext() responseObserver'а, чтобы отправить его клиенту.

Наконец, нам нужно вызватьonCompleted(), чтобы указать, что мы закончили работу с RPC, иначе соединение будет зависнуть, и клиент просто будет ждать поступления дополнительной информации.

6.2. Запуск сервера Grpc

Далее нам нужно запустить сервер gRPC для прослушивания входящих запросов:

public class GrpcServer {
    public static void main(String[] args) {
        Server server = ServerBuilder
          .forPort(8080)
          .addService(new HelloServiceImpl()).build();

        server.start();
        server.awaitTermination();
    }
}

Здесь мы снова используем построитель для создания сервера gRPC на порту 8080 и добавления службыHelloServiceImpl, которую мы определили. start() запустит сервер. В нашем примере мы вызовемawaitTermination(), чтобы сервер работал на переднем плане, блокируя приглашение.

7. Создание клиента

gRPC provides a channel construct which abstracts out the underlying details, такие как соединение, пул соединений, балансировка нагрузки и т. д.

Мы создадим канал, используяManagedChannelBuilder. Здесь мы указываем адрес сервера и порт.

Мы будем использовать обычный текст без шифрования:

public class GrpcClient {
    public static void main(String[] args) {
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8080)
          .usePlaintext()
          .build();

        HelloServiceGrpc.HelloServiceBlockingStub stub
          = HelloServiceGrpc.newBlockingStub(channel);

        HelloResponse helloResponse = stub.hello(HelloRequest.newBuilder()
          .setFirstName("example")
          .setLastName("gRPC")
          .build());

        channel.shutdown();
    }
}

Затем нам нужно создать заглушку, которую мы будем использовать для фактического удаленного вызоваhello(). The stub is the primary way for clients to interacts with the server. При использовании автоматической генерации заглушек класс заглушки будет иметь конструкторы для обертывания канала.

Здесь мы используем блокирующую / синхронную заглушку, так что вызов RPC ожидает ответа сервера и либо возвращает ответ, либо вызывает исключение. Существует два других типа заглушек, предоставляемых gRPC, которые облегчают неблокирующие / асинхронные вызовы.

Наконец, пора сделать RPC-вызовhello(). Здесь мы передаемHelloRequest. Мы можем использовать автоматически сгенерированные сеттеры для установки атрибутовfirstName,lastName объектаHelloRequest.

Мы возвращаем объектHelloResponse, возвращенный с сервера.

8. Заключение

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

Как обычно, вы найдете источникиover on GitHub.