Јава гРПЦ од нуле

Хајде да истражимо како да имплементирамо гРПЦ у Јави.

гРПЦ (Гоогле Ремоте Процедуре Цалл): гРПЦ је РПЦ архитектура отвореног кода коју је развио Гоогле да би омогућио комуникацију велике брзине између микросервиса. гРПЦ омогућава програмерима да интегришу услуге написане на различитим језицима. гРПЦ користи Протобуф формат за размену порука (Протоцол Буфферс), високо ефикасан, високо упакован формат за размену порука за серијализацију структурираних података.

За неке случајеве употребе, гРПЦ АПИ може бити ефикаснији од РЕСТ АПИ-ја.

Хајде да покушамо да напишемо сервер на гРПЦ. Прво, треба да напишемо неколико .прото датотека које описују услуге и моделе (ДТО). За једноставан сервер, користићемо ПрофилеСервице и ПрофилеДесцриптор.

ПрофилеСервице изгледа овако:

syntax = "proto3";
package com.deft.grpc;
import "google/protobuf/empty.proto";
import "profile_descriptor.proto";
service ProfileService {
  rpc GetCurrentProfile (google.protobuf.Empty) returns (ProfileDescriptor) {}
  rpc clientStream (stream ProfileDescriptor) returns (google.protobuf.Empty) {}
  rpc serverStream (google.protobuf.Empty) returns (stream ProfileDescriptor) {}
  rpc biDirectionalStream (stream ProfileDescriptor) returns (stream 	ProfileDescriptor) {}
}

гРПЦ подржава различите опције комуникације клијент-сервер. Све ћемо их разложити:

  • Нормални позив сервера – захтев/одговор.
  • Стримовање са клијента на сервер.
  • Стримовање са сервера на клијента.
  • И, наравно, двосмерни ток.

Услуга ПрофилеСервице користи ПрофилеДесцриптор, који је наведен у одељку за увоз:

syntax = "proto3";
package com.deft.grpc;
message ProfileDescriptor {
  int64 profile_id = 1;
  string name = 2;
}
  • инт64 је дуг за Јава. Нека ИД профила припада.
  • Стринг – баш као у Јави, ово је стринг променљива.

Можете користити Градле или мавен да направите пројекат. Мени је згодније да користим мавен. И даље ће бити код који користи мавен. Ово је довољно важно да се каже јер ће за Градле будућа генерација .прото-а бити мало другачија, а датотека за изградњу ће морати да се другачије конфигурише. Да бисмо написали једноставан гРПЦ сервер, потребна нам је само једна зависност:

<dependency>
    <groupId>io.github.lognet</groupId>
    <artifactId>grpc-spring-boot-starter</artifactId>
    <version>4.5.4</version>
</dependency>

Просто је невероватно. Овај стартер ради огроман посао за нас.

  Користите Омнисенд за повећање продаје помоћу аутоматизације, СМС-а и маркетинга путем е-поште

Пројекат који ћемо креирати ће изгледати отприлике овако:

Потребна нам је ГрпцСерверАпплицатион да покренемо Спринг Боот апликацију. И ГрпцПрофилеСервице, који ће имплементирати методе из .прото сервиса. Да бисте користили протокол и генерисали класе из написаних .прото датотека, додајте протобуф-мавен-плугин у пом.кмл. Одељак за изградњу ће изгледати овако:

<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>
                    <protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
                    <outputDirectory>${basedir}/target/generated-sources/grpc-java</outputDirectory>
                    <protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.38.0:exe:${os.detected.classifier}</pluginArtifact>
                    <clearOutputDirectory>false</clearOutputDirectory>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
  • протоСоурцеРоот – специфицирање директоријума у ​​коме се налазе .прото датотеке.
  • оутпутДирецтори – изаберите директоријум у коме ће се датотеке генерисати.
  • цлеарОутпутДирецтори – ознака која означава да се генерисане датотеке не бришу.

У овој фази можете направити пројекат. Затим морате да одете у фасциклу коју смо навели у излазном директоријуму. Генерисани фајлови ће бити тамо. Сада можете постепено имплементирати ГрпцПрофилеСервице.

Декларација класе ће изгледати овако:

@GRpcService
public class GrpcProfileService extends ProfileServiceGrpc.ProfileServiceImplBase

ГРпцСервице напомена – Означава класу као грпц-сервице беан.

Пошто смо наследили нашу услугу од ПрофилеСервицеГрпц, ПрофилеСервицеИмплБасе, можемо заменити методе родитељске класе. Први метод који ћемо заменити је гетЦуррентПрофиле:

    @Override
    public void getCurrentProfile(Empty request, StreamObserver<ProfileDescriptorOuterClass.ProfileDescriptor> responseObserver) {
        System.out.println("getCurrentProfile");
        responseObserver.onNext(ProfileDescriptorOuterClass.ProfileDescriptor
                .newBuilder()
                .setProfileId(1)
                .setName("test")
                .build());
        responseObserver.onCompleted();
    }

Да бисте одговорили клијенту, потребно је да позовете методу онНект на пренесеном СтреамОбсерверу. Након слања одговора, пошаљите сигнал клијенту да је сервер завршио рад на Завршено. Када шаљете захтев серверу гетЦуррентПрофиле, одговор ће бити:

{
  "profile_id": "1",
  "name": "test"
}

Затим, хајде да погледамо ток сервера. Са овим приступом размени порука, клијент шаље захтев серверу, сервер одговара клијенту низом порука. На пример, шаље пет захтева у петљи. Када је слање завршено, сервер шаље поруку клијенту о успешном завршетку стрима.

  Поправи грешку Форза Хоризон 4 Ова апликација не може да отвори

Метода замењеног серверског стрима ће изгледати овако:

@Override
    public void serverStream(Empty request, StreamObserver<ProfileDescriptorOuterClass.ProfileDescriptor> responseObserver) {
        for (int i = 0; i < 5; i++) {
            responseObserver.onNext(ProfileDescriptorOuterClass.ProfileDescriptor
                    .newBuilder()
                    .setProfileId(i)
                    .build());
        }
        responseObserver.onCompleted();
    }

Тако ће клијент добити пет порука са ПрофилеИд-ом, једнаким броју одговора.

{
  "profile_id": "0",
  "name": ""
}
{
  "profile_id": "1",
  "name": ""
}
…
{
  "profile_id": "4",
  "name": ""
}

Клијентски ток је веома сличан стриму сервера. Тек сада клијент преноси ток порука, а сервер их обрађује. Сервер може одмах да обради поруке или да сачека све захтеве клијента и затим их обради.

    @Override
    public StreamObserver<ProfileDescriptorOuterClass.ProfileDescriptor> clientStream(StreamObserver<Empty> responseObserver) {
        return new StreamObserver<>() {

            @Override
            public void onNext(ProfileDescriptorOuterClass.ProfileDescriptor profileDescriptor) {
                log.info("ProfileDescriptor from client. Profile id: {}", profileDescriptor.getProfileId());
            }

            @Override
            public void onError(Throwable throwable) {

            }

            @Override
            public void onCompleted() {
                responseObserver.onCompleted();
            }
        };
    }

У Цлиент стреам-у, потребно је да вратите СтреамОбсервер клијенту, коме ће сервер примати поруке. Метода онЕррор ће бити позвана ако је дошло до грешке у току. На пример, нетачно је прекинут.

За имплементацију двосмерног тока потребно је комбиновати креирање тока са сервера и клијента.

@Override
    public StreamObserver<ProfileDescriptorOuterClass.ProfileDescriptor> biDirectionalStream(
            StreamObserver<ProfileDescriptorOuterClass.ProfileDescriptor> 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();
            }
        };
    } 

У овом примеру, као одговор на поруку клијента, сервер ће вратити профил са повећаним бројем поена.

Закључак

Покрили смо основне опције за размену порука између клијента и сервера користећи гРПЦ: имплементирани серверски ток, клијентски ток, двосмерни ток.

Чланак је написао Сергеј Голитсин