Geekflare wird von unserem Publikum unterstützt. Wir können Affiliate-Provisionen durch den Kauf von Links auf dieser Website verdienen.
Teilen:

Java-gRPC von Scratch

Java-Optionen
Invicti Web Application Security Scanner – die einzige Lösung, die eine automatische Verifizierung von Schwachstellen mit Proof-Based Scanning™ bietet.

Sehen wir uns an, wie Sie gRPC in Java implementieren.

gRPC (Google Remote Procedure Call): gRPC ist eine Open-Source-RPC-Architektur, die von Google entwickelt wurde, um eine Hochgeschwindigkeitskommunikation zwischen Microservices zu ermöglichen. gRPC ermöglicht es Entwicklern, in verschiedenen Sprachen geschriebene Dienste zu integrieren. gRPC verwendet das Protobuf-Messaging-Format (Protocol Buffers), ein hocheffizientes, stark gepacktes Messaging-Format zum Serialisieren strukturierter Daten.

Für einige Anwendungsfälle kann die gRPC-API effizienter sein als die REST API.

Versuchen wir, einen Server auf gRPC zu schreiben. Zuerst müssen wir mehrere schreiben .proto Dateien, die Dienste und Modelle (DTO) beschreiben. Für einen einfachen Server verwenden wir ProfileService und ProfileDescriptor.

ProfileService sieht so aus:

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

gRPC unterstützt eine Vielzahl von Client-Server-Kommunikationsoptionen. Wir werden sie alle aufschlüsseln:

  • Normaler Serveraufruf – Anfrage/Antwort.
  • Streaming vom Client zum Server.
  • Streaming vom Server zum Client.
  • Und natürlich der bidirektionale Stream.

Der ProfileService-Dienst verwendet den ProfileDescriptor, der im Importabschnitt angegeben ist:

syntax = "proto3";
package com.deft.grpc;
message ProfileDescriptor {
  int64 profile_id = 1;
  string name = 2;
}
  • int64 ist Long für Java. Lassen Sie die Profil-ID gehören.
  • Schnur – Genau wie in Java ist dies eine String-Variable.

Sie können Gradle oder Maven verwenden, um das Projekt zu erstellen. Es ist bequemer für mich, Maven zu verwenden. Und weiter wird der Code mit Maven sein. Dies ist wichtig genug, um zu sagen, dass die zukünftige Generation der .proto für Gradle etwas anders sein wird und die Build-Datei anders konfiguriert werden muss. Um einen einfachen gRPC-Server zu schreiben, benötigen wir nur eine Abhängigkeit:

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

Es ist einfach unglaublich. Dieser Starter macht uns enorm viel Arbeit.

Das von uns erstellte Projekt sieht in etwa so aus:

Wir benötigen GrpcServerApplication, um die Spring Boot-Anwendung zu starten. Und GrpcProfileService, das Methoden aus dem .proto Bedienung. Um protoc zu verwenden und Klassen aus geschriebenen .proto-Dateien zu generieren, fügen Sie protobuf-maven-plugin zu pom.xml hinzu. Der Build-Bereich sieht so aus:

<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 – Angabe des Verzeichnisses, in dem sich die .proto-Dateien befinden.
  • Ausgabe Verzeichnis – Wählen Sie das Verzeichnis aus, in dem die Dateien generiert werden sollen.
  • clearOutputDirectory – ein Flag, das angibt, generierte Dateien nicht zu löschen.

In dieser Phase können Sie ein Projekt erstellen. Als nächstes müssen Sie zu dem Ordner gehen, den wir im Ausgabeverzeichnis angegeben haben. Die generierten Dateien werden dort sein. Jetzt können Sie nach und nach umsetzen GrpcProfileService.

Die Klassendeklaration sieht wie folgt aus:

@GRpcService
public class GrpcProfileService extends ProfileServiceGrpc.ProfileServiceImplBase

GRpcService annotation – Markiert die Klasse als grpc-service-Bean.

Da wir unseren Service erben von ProfileServiceGrpc, ProfileServiceImplBase, können wir die Methoden der Elternklasse überschreiben. Die erste Methode, die wir überschreiben werden, ist 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();
    }

Um dem Kunden zu antworten, müssen Sie den aufWeiter -Methode auf dem übergebenen StreamObserver. Senden Sie nach dem Senden der Antwort ein Signal an den Client, dass der Server seine Arbeit beendet hat onAbgeschlossen. Beim Senden einer Anfrage an den getCurrentProfile-Server lautet die Antwort:

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

Als nächstes werfen wir einen Blick auf den Server-Stream. Bei diesem Messaging-Ansatz sendet der Client eine Anfrage an den Server, der Server antwortet dem Client mit einem Nachrichtenstrom. Es sendet beispielsweise fünf Anfragen in einer Schleife. Wenn das Senden abgeschlossen ist, sendet der Server eine Nachricht an den Client über den erfolgreichen Abschluss des Streams.

Die überschriebene Server-Stream-Methode sieht wie folgt aus:

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

Somit erhält der Client fünf Nachrichten mit einer ProfileId, die der Antwortnummer entspricht.

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

Der Client-Stream ist dem Server-Stream sehr ähnlich. Erst jetzt sendet der Client einen Nachrichtenstrom und der Server verarbeitet sie. Der Server kann Nachrichten sofort verarbeiten oder auf alle Anfragen des Clients warten und diese dann verarbeiten.

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

Im Client-Stream müssen Sie die StreamObserver an den Client, an den der Server Nachrichten erhält. Die Methode onError wird aufgerufen, wenn im Stream ein Fehler aufgetreten ist. Zum Beispiel wurde es falsch beendet.

Um einen bidirektionalen Stream zu implementieren, ist es notwendig, die Erstellung eines Streams vom Server und dem Client zu kombinieren.

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

In diesem Beispiel gibt der Server als Antwort auf die Nachricht des Clients ein Profil mit einem erhöhten zurück Punktzahl.

Fazit

Wir haben die grundlegenden Optionen für die Nachrichtenübermittlung zwischen einem Client und einem Server unter Verwendung von . behandelt gRPC: implementierter Server-Stream, Client-Stream, bidirektionaler Stream.

Der Artikel wurde von Sergey Golitsyn . geschrieben

Danke an unsere Sponsoren
Weitere großartige Lektüre zum Thema Entwicklung
Treiben Sie Ihr Geschäft an
Einige der Tools und Dienste, die Ihr Unternehmen beim Wachstum unterstützen.
  • Invicti verwendet das Proof-Based Scanning™, um die identifizierten Schwachstellen automatisch zu verifizieren und innerhalb weniger Stunden umsetzbare Ergebnisse zu generieren.
    Versuchen Sie es mit Invicti
  • Web-Scraping, Wohn-Proxy, Proxy-Manager, Web-Unlocker, Suchmaschinen-Crawler und alles, was Sie zum Sammeln von Webdaten benötigen.
    Versuchen Sie es mit Brightdata
  • Semrush ist eine All-in-One-Lösung für digitales Marketing mit mehr als 50 Tools in den Bereichen SEO, Social Media und Content-Marketing.
    Versuchen Sie es mit Semrush
  • Intruder ist ein Online-Schwachstellenscanner, der Cyber-Sicherheitslücken in Ihrer Infrastruktur findet, um kostspielige Datenschutzverletzungen zu vermeiden.
    MIT DER INTELLIGENTEN SCHADENKALKULATION VON Intruder