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ó.
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.
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.
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