Tehnografi.com - Технологические новости, обзоры и советы
[adinserter block="67"]

Java gRPC с нуля

Примечание. Следующая статья поможет вам: Java gRPC с нуля

Давайте рассмотрим, как реализовать gRPC в Java.

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

В некоторых случаях API gRPC может быть более эффективным, чем REST API.

Попробуем написать сервер на gRPC. Во-первых, нам нужно написать несколько файлов .proto, описывающих сервисы и модели (DTO). Для простого сервера мы будем использовать ProfileService и ProfileDescriptor.

ProfileService выглядит следующим образом:

синтаксис = “прото3”; пакет com.deft.grpc; импортировать “google/protobuf/empty.proto”; импортировать “profile_descriptor.proto”; service ProfileService { rpc GetCurrentProfile (google.protobuf.Empty) возвращает (ProfileDescriptor) {} rpc clientStream (поток ProfileDescriptor) возвращает (google.protobuf.Empty) {} rpc serverStream (google.protobuf.Empty) возвращает (поток ProfileDescriptor) {} rpc biDirectionalStream (поток ProfileDescriptor) возвращает (поток ProfileDescriptor) {} }

gRPC поддерживает множество вариантов связи клиент-сервер. Мы сломаем их все:

  • Обычный вызов сервера — запрос/ответ.
  • Потоковая передача с клиента на сервер.
  • Трансляция с сервера на клиент.
  • И, конечно же, двунаправленный поток.
  • Служба ProfileService использует ProfileDescriptor, указанный в разделе импорта:

    синтаксис = “прото3”; пакет com.deft.grpc; сообщение ProfileDescriptor { int64 profile_id = 1; имя строки = 2; }

  • int64 долго для Java. Пусть идентификатор профиля принадлежит.
  • Нить — как и в Java, это строковая переменная.
  • Вы можете использовать Gradle или maven для сборки проекта. Мне удобнее использовать maven. А дальше будет код с использованием maven. Это достаточно важно, чтобы сказать, потому что для Gradle будущее поколение .proto будет немного другим, и файл сборки нужно будет настроить по-другому. Чтобы написать простой сервер gRPC, нам нужна только одна зависимость:

    io.github.lognet grpc-spring-boot-starter 4.5.4

    Это просто невероятно. Этот стартер делает для нас колоссальный объем работы.

    Проект, который мы создадим, будет выглядеть примерно так:

    Нам нужно GrpcServerApplication для запуска приложения Spring Boot. И GrpcProfileService, который будет реализовывать методы из сервиса .proto. Чтобы использовать protoc и создавать классы из записанных файлов .proto, добавьте protobuf-maven-plugin в pom.xml. Раздел сборки будет выглядеть так:

    kr.motd.maven os-maven-plugin 1.6.2 org.xolstice.maven.plugins protobuf-maven-plugin 0.6.1 $ {project.basedir}/src/main/proto ${basedir}/target/generated-sources/grpc-java com.google.protobuf:protoc:3.12.0 :exe:${os.detected.classifier} grpc-java io.grpc:protoc-gen-grpc-java:1.38.0:exe:${os. обнаруженный.классификатор} false compile compile-custom

  • protoSourceRoot – указание каталога, в котором находятся файлы .proto.
  • выходной каталог – выберите каталог, в котором будут созданы файлы.
  • ClearOutputDirectory – флаг, указывающий не очищать сгенерированные файлы.
  • На этом этапе можно построить проект. Далее нужно перейти в папку, которую мы указали в выходном каталоге. Сгенерированные файлы будут там. Теперь можно постепенно внедрять Грпкпрофилесервице.

    Объявление класса будет выглядеть так:

    Открытый класс @GRpcService GrpcProfileService расширяет ProfileServiceGrpc.ProfileServiceImplBase

    GRpcService annotation — помечает класс как bean-компонент grpc-service.

    Поскольку мы наследуем наш сервис от ПрофильСервисГрпк, ПрофильСервисИмплБасе, мы можем переопределить методы родительского класса. Первый метод, который мы переопределим, это получить текущий профиль:

    @Override public void getCurrentProfile(Пустой запрос, StreamObserver responseObserver) { System.out.println(“getCurrentProfile”); responseObserver.onNext(ProfileDescriptorOuterClass.ProfileDescriptor .newBuilder() .setProfileId(1) .setName(“test”) .build()); responseObserver.onCompleted(); }

    Для ответа клиенту необходимо позвонить onNext метод переданного StreamObserver. После отправки ответа отправить сигнал клиенту о том, что сервер закончил работу onCompleted. При отправке запроса на сервер getCurrentProfile ответ будет таким:

    { “profile_id”: “1”, “имя”: “тест” }

    Далее, давайте взглянем на поток сервера. При таком подходе к обмену сообщениями клиент отправляет запрос на сервер, сервер отвечает клиенту потоком сообщений. Например, он отправляет пять запросов в цикле. Когда отправка завершена, сервер отправляет клиенту сообщение об успешном завершении потока.

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

    @Override public void serverStream (пустой запрос, StreamObserver responseObserver) { for (int i = 0; i Таким образом, клиент получит пять сообщений с ProfileId, равным номеру ответа.

    { “profile_id”: “0”, “name”: “” } { “profile_id”: “1”, “name”: “” } … { “profile_id”: “4”, “name”: “” }

    Клиентский поток очень похож на серверный поток. Только теперь клиент передает поток сообщений, а сервер их обрабатывает. Сервер может обрабатывать сообщения немедленно или ждать всех запросов от клиента, а затем обрабатывать их.

    @Override public StreamObserver clientStream(StreamObserver responseObserver) { return new StreamObserver() { @Override public void onNext(ProfileDescriptorOuterClass.ProfileDescriptor profileDescriptor) { log.info(“ProfileDescriptor from client. Идентификатор профиля: {}”, profileDescriptor.getProfileId()); } @Override public void onError(Throwable throwable) { } @Override public void onCompleted() { responseObserver.onCompleted(); } }; }

    В клиентском потоке вам нужно вернуть StreamObserver клиенту, которому сервер будет получать сообщения. Метод onError будет вызываться, если в потоке произошла ошибка. Например, он завершился неправильно.

    Для реализации двунаправленного потока необходимо совместить создание потока с сервера и клиента.

    @Override public StreamObserver biDirectionalStream( StreamObserver responseObserver) { return new StreamObserver() { int pointCount = 0; @Override public void onNext(ProfileDescriptorOuterClass.ProfileDescriptor profileDescriptor) { log.info(“biDirectionalStream, pointCount {}”, pointCount); responseObserver.onNext(ProfileDescriptorOuterClass.ProfileDescriptor .newBuilder() .setProfileId(pointCount++) .build()); } @Override public void onError(Throwable throwable) { } @Override public void onCompleted() { responseObserver.onCompleted(); } }; }

    В этом примере в ответ на сообщение клиента сервер вернет профиль с повышенным точкаКоличество.

    Вывод

    Мы рассмотрели основные варианты обмена сообщениями между клиентом и сервером с помощью gRPC: реализован серверный поток, клиентский поток, двунаправленный поток.