Einführung

gRPC (gRPC Remote Procedure Call) ist ein modernes Open-Source-Remote-Prozeduraufrufs (RPC) -Framework, das von Google entwickelt wurde. Es ermöglicht die einfache Erstellung von verteilten Anwendungen und Diensten, indem es die Kommunikation zwischen ihnen vereinfacht und optimiert. gRPC basiert auf dem Protokoll Buffers (protobuf), einem effizienten binären Serialisierungsformat, das von Google ebenfalls entwickelt wurde.

Warum gRPC?

Die Hauptgründe für die Verwendung von gRPC sind:

  1. Leistung: Durch die Verwendung von Protokoll Buffers ist gRPC äußerst performant und ressourcenschonend.
  2. Sprachenunabhängigkeit: gRPC unterstützt eine Vielzahl von Programmiersprachen, darunter Java, C++, Python, Go, Ruby, und viele mehr.
  3. Plattformübergreifend: gRPC kann auf verschiedenen Plattformen eingesetzt werden, von mobilen Geräten bis zu Servern.
  4. Stream-Unterstützung: Neben einfachen RPCs unterstützt gRPC auch Client-Streaming, Server-Streaming und bidirektionales Streaming.
  5. Automatische Code-Generierung: Durch die Definition von Diensten in einer Protobuf-Datei kann gRPC automatisch Client- und Server-Code generieren.

Grundlagen der Protokoll Buffers (protobuf)

Bevor wir tiefer in gRPC einsteigen, ist es wichtig, die Grundlagen der Protokoll Buffers zu verstehen. Protobuf ist ein Interface Definition Language (IDL), das zur Beschreibung der Struktur von Daten und der RPC-Dienste verwendet wird.

Eine Protobuf-Datei hat die Endung .proto und definiert Nachrichten und Dienste. Hier ist ein einfaches Beispiel:

syntax = "proto3";

package example;

message HelloRequest {
    string name = 1;
}

message HelloReply {
    string message = 1;
}

service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply);
}Code-Sprache: JavaScript (javascript)

In diesem Beispiel definieren wir einen Dienst Greeter mit einer Methode SayHello, die eine HelloRequest empfängt und eine HelloReply zurückgibt.

Erstellen eines gRPC-Dienstes in Java

Voraussetzungen

Um gRPC in Java zu verwenden, benötigen Sie die folgenden Abhängigkeiten in Ihrem build.gradle (für Gradle) oder pom.xml (für Maven):

plugins {
    id 'java'
    id 'com.google.protobuf' version '0.8.16'
}

dependencies {
    implementation 'io.grpc:grpc-netty-shaded:1.40.1'
    implementation 'io.grpc:grpc-protobuf:1.40.1'
    implementation 'io.grpc:grpc-stub:1.40.1'
    compileOnly 'org.apache.tomcat:annotations-api:6.0.53'
}

protobuf {
    protoc {
        artifact = 'com.google.protobuf:protoc:3.17.3'
    }
    plugins {
        grpc {
            artifact = 'io.grpc:protoc-gen-grpc-java:1.40.1'
        }
    }
    generateProtoTasks {
        all().each { task ->
            task.plugins {
                grpc {}
            }
        }
    }
}Code-Sprache: JavaScript (javascript)

Protobuf-Kompilierung

Speichern Sie die oben angegebene Protobuf-Datei example.proto im Verzeichnis src/main/proto. Führen Sie dann den folgenden Befehl aus, um die Java-Klassen zu generieren:

./gradlew build

Implementierung des gRPC-Servers

Erstellen Sie nun die Implementierung des gRPC-Dienstes:

package example;

import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;

import java.io.IOException;

public class GreeterServer {

    public static void main(String[] args) throws IOException, InterruptedException {
        Server server = ServerBuilder.forPort(50051)
                .addService(new GreeterImpl())
                .build()
                .start();

        System.out.println("Server started, listening on " + server.getPort());
        server.awaitTermination();
    }

    static class GreeterImpl extends GreeterGrpc.GreeterImplBase {

        @Override
        public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
            String greeting = "Hello, " + request.getName();
            HelloReply reply = HelloReply.newBuilder().setMessage(greeting).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }
    }
}Code-Sprache: JavaScript (javascript)

In diesem Beispiel implementieren wir den Greeter-Dienst, der die Methode sayHello überschreibt. Der Server wird auf Port 50051 gestartet und wartet auf Anfragen.

Implementierung des gRPC-Clients

Nun erstellen wir einen einfachen gRPC-Client, um den Dienst zu nutzen:

package example;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

public class GreeterClient {

    public static void main(String[] args) throws InterruptedException {
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)
                .usePlaintext()
                .build();

        GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel);

        HelloRequest request = HelloRequest.newBuilder().setName("World").build();
        HelloReply response = stub.sayHello(request);

        System.out.println(response.getMessage());

        channel.shutdown();
    }
}Code-Sprache: JavaScript (javascript)

Der Client stellt eine Verbindung zum Server her und sendet eine HelloRequest mit dem Namen „World“. Der Server antwortet mit einer HelloReply, die die Nachricht „Hello, World“ enthält.

Erweiterte Funktionen von gRPC

gRPC bietet neben einfachen RPCs auch erweiterte Funktionen wie Streaming und Authentifizierung.

Streaming

Es gibt vier Arten von gRPC-Methoden:

  1. Unary RPC: Eine einzelne Anfrage führt zu einer einzelnen Antwort (wie im obigen Beispiel).
  2. Server-Streaming RPC: Eine einzelne Anfrage führt zu einer Folge von Antworten.
  3. Client-Streaming RPC: Eine Folge von Anfragen führt zu einer einzelnen Antwort.
  4. Bidirektionales Streaming RPC: Eine Folge von Anfragen führt zu einer Folge von Antworten.

Hier ist ein Beispiel für einen serverseitigen Streaming-Dienst:

service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply);
    rpc StreamHello (HelloRequest) returns (stream HelloReply);
}

Implementierung des serverseitigen Streamings:

static class GreeterImpl extends GreeterGrpc.GreeterImplBase {

    @Override
    public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
        String greeting = "Hello, " + request.getName();
        HelloReply reply = HelloReply.newBuilder().setMessage(greeting).build();
        responseObserver.onNext(reply);
        responseObserver.onCompleted();
    }

    @Override
    public void streamHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
        for (int i = 0; i < 5; i++) {
            String greeting = "Hello, " + request.getName() + " - " + i;
            HelloReply reply = HelloReply.newBuilder().setMessage(greeting).build();
            responseObserver.onNext(reply);
            try {
                Thread.sleep(1000); // Simuliert eine Verzögerung
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        responseObserver.onCompleted();
    }
}Code-Sprache: JavaScript (javascript)

Im obigen Beispiel sendet die Methode streamHello fünf Nachrichten mit einer Sekunde Verzögerung an den Client.

Authentifizierung

gRPC unterstützt verschiedene Authentifizierungsmethoden, einschließlich TLS und Token-basierter Authentifizierung. Hier ist ein Beispiel für die Einrichtung eines Servers mit TLS:

Server server = ServerBuilder.forPort(50051)
        .useTransportSecurity(new File("server.crt"), new File("server.pem"))
        .addService(new GreeterImpl())
        .build()
        .start();Code-Sprache: JavaScript (javascript)

Hierbei müssen Sie die entsprechenden Zertifikatsdateien (server.crt und server.pem) angeben.

Fazit

gRPC ist ein leistungsstarkes und flexibles Framework für die Entwicklung verteilter Systeme. Durch seine effiziente Nutzung von Protokoll Buffers, die Unterstützung mehrerer Programmiersprachen und die vielfältigen Kommunikationsmethoden (einschließlich Streaming) bietet gRPC eine moderne Lösung für die Herausforderungen moderner vernetzter Anwendungen. Mit den hier vorgestellten Grundlagen und Beispielen können Sie beginnen, eigene gRPC-Dienste und -Clients in Java zu erstellen und die Vorteile dieser Technologie in Ihren Projekten zu nutzen.