Java gRPC a semmiből

Fedezzük fel, hogyan implementálhatjuk a gRPC-t Java-ban.

gRPC (Google Remote Procedure Call): A gRPC a Google által kifejlesztett nyílt forráskódú RPC architektúra, amely lehetővé teszi a mikroszolgáltatások közötti nagy sebességű kommunikációt. A gRPC lehetővé teszi a fejlesztők számára, hogy integrálják a különböző nyelveken írt szolgáltatásokat. A gRPC a Protobuf üzenetküldési formátumot (Protocol Buffers) használja, amely egy rendkívül hatékony, rendkívül csomagolt üzenetformátum a strukturált adatok sorba rendezésére.

Egyes felhasználási esetekben a gRPC API hatékonyabb lehet, mint a REST API.

Próbáljunk meg szervert írni a gRPC-n. Először is több .proto fájlt kell írnunk, amelyek leírják a szolgáltatásokat és a modelleket (DTO). Egy egyszerű szerverhez a ProfileService és a ProfileDescriptor szolgáltatást használjuk.

A ProfileService így néz ki:

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

A gRPC számos kliens-szerver kommunikációs lehetőséget támogat. Mindet lebontjuk:

  • Normál szerverhívás – kérés/válasz.
  • Streaming kliensről szerverre.
  • Streaming szerverről kliensre.
  • És persze a kétirányú folyam.

A ProfileService szolgáltatás a ProfileDescriptor-t használja, amely az importálás szakaszban van megadva:

syntax = "proto3";
package com.deft.grpc;
message ProfileDescriptor {
  int64 profile_id = 1;
  string name = 2;
}
  • Az int64 hosszú a Java számára. Tartsa a profilazonosítót.
  • String – akárcsak a Java-ban, ez is egy karakterlánc-változó.
  Hogyan küldjünk kódot Skype-on megfelelő formázással

A projekt felépítéséhez használhatja a Gradle-t vagy a Maven-t. Nekem kényelmesebb a maven használata. És további lesz a maven kódot használó kód. Ez elég fontos ahhoz, hogy elmondjam, mert a Gradle esetében a .proto jövőbeli generációja kissé más lesz, és az összeállítási fájlt másképp kell konfigurálni. Egy egyszerű gRPC szerver írásához egyetlen függőségre van szükségünk:

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

Egyszerűen hihetetlen. Ez az indító óriási munkát végez értünk.

A projekt, amelyet létrehozunk, valahogy így fog kinézni:

A Spring Boot alkalmazás elindításához GrpcServerApplication szükséges. És a GrpcProfileService, amely a .proto szolgáltatás metódusait fogja megvalósítani. A protokoll használatához és osztályok generálásához írott .proto fájlokból adja hozzá a protobuf-maven-plugin-t a pom.xml fájlhoz. Az építési rész így fog kinézni:

<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>
  • protoSourceRoot – megadja azt a könyvtárat, ahol a .proto fájlok találhatók.
  • outputDirectory – válassza ki a könyvtárat, ahol a fájlok létrejönnek.
  • clearOutputDirectory – jelző, amely azt jelzi, hogy ne törölje a generált fájlokat.

Ebben a szakaszban építhet egy projektet. Ezután a kimeneti könyvtárban megadott mappába kell lépnie. A generált fájlok ott lesznek. Most fokozatosan bevezetheti a GrpcProfileService-t.

  Hogyan találhat valakit a POF-on e-mailben

Az osztálydeklaráció így fog kinézni:

@GRpcService
public class GrpcProfileService extends ProfileServiceGrpc.ProfileServiceImplBase

GRpcService annotáció – Az osztályt grpc-service komponensként jelöli meg.

Mivel szolgáltatásunkat a ProfileServiceGrpc-től, a ProfileServiceImplBase-től örököljük, felülírhatjuk a szülőosztály metódusait. Az első módszer, amelyet felülírunk, a getCurrentProfile:

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

Az ügyfélnek való válaszadáshoz meg kell hívnia az onNext metódust az átadott StreamObserveren. A válasz elküldése után küldjön egy jelzést a kliensnek, hogy a szerver befejezte a munkát a Befejezve. Amikor kérést küld a getCurrentProfile szervernek, a válasz a következő lesz:

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

Ezután vessünk egy pillantást a szerver adatfolyamára. Ezzel az üzenetküldési megközelítéssel a kliens kérést küld a szervernek, a szerver üzenetfolyammal válaszol a kliensnek. Például öt kérést küld egy ciklusban. Amikor a küldés befejeződött, a szerver üzenetet küld a kliensnek a stream sikeres befejezéséről.

A felülírt szerver adatfolyam módszere így fog kinézni:

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

Így a kliens öt, a válaszszámmal megegyező ProfileId-vel rendelkező üzenetet kap.

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

A kliens adatfolyam nagyon hasonlít a szerver adatfolyamhoz. Csak most a kliens üzenetfolyamot továbbít, és a szerver feldolgozza azokat. A szerver azonnal feldolgozhatja az üzeneteket, vagy megvárhatja a kliens összes kérését, majd feldolgozhatja azokat.

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

A Kliens adatfolyamban vissza kell küldenie a StreamObservert a kliensnek, amelyhez a szerver üzeneteket fog kapni. Az onError metódus akkor kerül meghívásra, ha hiba történt az adatfolyamban. Például hibásan fejeződött be.

  Útmutató kezdőknek a mérföldkő diagramról [+6 Templates]

A kétirányú adatfolyam megvalósításához kombinálni kell egy adatfolyamot a szerverről és a kliensről.

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

Ebben a példában az ügyfél üzenetére válaszul a szerver egy megnövelt pontszámmal rendelkező profilt ad vissza.

Következtetés

Áttekintettük a kliens és a kiszolgáló közötti, gRPC-t használó üzenetkezelés alapvető lehetőségeit: implementált szerver adatfolyam, kliens adatfolyam, kétirányú adatfolyam.

A cikket Sergey Golitsyn írta