Wichtige Punkte für die Einführung von gRPC in Java

Was ist gRPC?

Eine Übersicht über gRPC finden Sie im Eintrag hier (https://qiita.com/disc99/items/cfca50a32240284578bb).

In diesem Beitrag werde ich mich auf die Punkte konzentrieren, die beim Betrieb von gRPC von Belang sind, und auf den Fall der Implementierung in Java.

Entwicklungsfluss

In modernen Systemen nimmt die Kommunikation zwischen Systemen über APIs zu. In diesem Fall wird die Schnittstelle jedes Systems häufig in einem der folgenden Muster entwickelt.

Muster 1: Beschreiben Sie die Schnittstellendokumente (Spezifikationen usw.) manuell und implementieren Sie die Server- und Cliententwickler, um die Anforderungen zu erfüllen. Muster 2: Generieren Sie automatisch eine Schnittstellendokumentation aus serverseitigem Code und implementieren Sie den Client, um seine Anforderungen zu erfüllen Muster 3: Schreiben Sie die Schnittstellendokumentation manuell, um automatisch Server- und Clientcode für jedes System zu generieren

Bei der Entwicklung mit gRPC werden die obigen 3 Muster verwendet. Diese Methode definiert das Schnittstellendokument vor und generiert automatisch eine Implementierung dafür.

Es gibt Verdienste wie.

GRPC in Java

Dies ist die einfachste gRPC-Implementierung in Java.

Projekt Einstellungen

build.gradle


buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'com.google.protobuf'

repositories {
    jcenter()
}

def grpcVersion = '1.21.0'

dependencies {
    compile "io.grpc:grpc-netty:${grpcVersion}"
    compile "io.grpc:grpc-protobuf:${grpcVersion}"
    compile "io.grpc:grpc-stub:${grpcVersion}"
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.8.0"
    }
    plugins {
        grpc {
            artifact = 'io.grpc:protoc-gen-grpc-java:1.21.0'
        }
    }
    generateProtoTasks {
        all()*.plugins {
            grpc {}
        }
    }
}

// for IDEA
sourceSets {
    main {
        java {
            srcDirs 'build/generated/source/proto/main/grpc'
            srcDirs 'build/generated/source/proto/main/java'
        }
    }
}

Definieren Sie die Schnittstelle mit .proto

src/main/proto/helloworld.proto


syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

Codegenerierung zur Verwendung aus Gradle

> ./gradlew generateProto

Implementieren Sie serverseitigen Code

Implementieren Sie den in ".proto" definierten Prozess in der Klasse, die "* Grpc. * ImplBase" erbt, und registrieren Sie ihn bei "addService" in "ServerBuilder".

DemoServer.java


class DemoServer {

    public static void main(String[] args) throws Exception {

        Server server = ServerBuilder.forPort(6565)
                .addService(new GreeterImpl())
                .build();

        server.start();

        server.awaitTermination();
    }

    static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
        @Override
        public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
            HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }
    }
}

Implementieren Sie clientseitigen Code

ManagedChannelBuilder erstellt einen Kanal für den Server und verwendet die automatisch generierte Stub-Klasse, um die Anforderung auszuführen.

DemoClient.java


class DemoClient {
    public static void main(String[] args) {
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 6565)
                .usePlaintext()
                .build();

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

        HelloRequest request = HelloRequest.newBuilder()
                .setName("Tom")
                .build();

        HelloReply reply = stub.sayHello(request);

        System.out.println("Reply: " + reply);
    }
}

Wenn Sie DemoServer starten und DemoClient ausführen, wird die folgende Ausgabe ausgegeben.

output


> Reply: message: "Hello Tom"

GRPC mit Spring Boot

Für Spring Boot gibt es einen Starter, der bequem zu verwenden ist.

Abhängigkeiten ändern

build.gradle


//...

dependencies {
//    compile "io.grpc:grpc-netty:${grpcVersion}"
//    compile "io.grpc:grpc-protobuf:${grpcVersion}"
//    compile "io.grpc:grpc-stub:${grpcVersion}"
	compile('org.lognet:grpc-spring-boot-starter:${grpcStarterVersion}')
}

//...

Spring Boot Server-Implementierung

Fügen Sie der Klasse, die "* Grpc. * ImplBase" erbt, "@ GRpcService" hinzu und implementieren Sie den in ".proto" definierten Prozess.

DemoServerApplication.java


@SpringBootApplication
class DemoServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoServerApplication.class, args);
	}

	@GRpcService
	public static class GreeterService extends GreeterGrpc.GreeterImplBase {
		@Override
		public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
            HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + request.getName()).build();
			responseObserver.onNext(reply);
			responseObserver.onCompleted();

		}
	}
}

gRPC-Kommunikationsmethode

Mit gRPC können Sie die bidirektionale Kommunikation über HTTP / 2 anstelle der herkömmlichen Einzelkommunikation verwenden.

Kommunikationsmethode Bild Überblick Verwenden
Unary RPC Kobito.wC31i8.png Eine Methode, die eine Antwort für eine Anforderung zurückgibt Allgemeine API, Kommunikation zwischen Anwendungen
Server streaming RPC Kobito.62GIoA.png Eine Methode, die mehrere Antworten auf eine Anforderung zurückgibt Wenn mehrere Daten vom Server übertragen werden, z. B. serverseitiger Push, Timeline, Feed-Zustellung usw.
Client streaming RPC Kobito.3xi5Yv.png Eine Methode, die eine Antwort auf mehrere Anforderungen zurückgibt Große Menge an Daten hochladen usw.
Bidirectional streaming RPC Kobito.sAGiM5.png Eine Methode, die mehrere Antworten auf mehrere Anforderungen zurückgibt Für bidirektionale Kommunikation wie Chat

Das Folgende ist ein Beispiel für jede Implementierung.

Proto-Datei

src/main/proto/helloworld.proto


// ...

service Greeter {
  rpc SayHelloUnary (HelloRequest) returns (HelloReply) {}
  rpc SayHelloServerStreaming (HelloRequest) returns (stream HelloReply) {}
  rpc SayHelloClientStreaming (stream HelloRequest) returns (HelloReply) {}
  rpc SayHelloBidirectionalStreaming (stream HelloRequest) returns (stream HelloReply) {}
}

// ...

Serverimplementierung

DemoServer.java


class DemoServer {

    public static void main(String[] args) throws Exception {
    	// ...
    }

	public static class GreeterService extends GreeterGrpc.GreeterImplBase {

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

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

		@Override
		public StreamObserver<HelloRequest> sayHelloClientStreaming(StreamObserver<HelloReply> responseObserver) {
			List<String> requests = new ArrayList<>();
			return new StreamObserver<HelloRequest>() {
				@Override
				public void onNext(HelloRequest request) {
					requests.add(request.getName());
				}
				@Override
				public void onError(Throwable t) {
					// ...
				}
				@Override
				public void onCompleted() {
					HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + requests.toString()).build();
					responseObserver.onNext(reply);
					responseObserver.onCompleted();
				}
			};
		}

		@Override
		public StreamObserver<HelloRequest> sayHelloBidirectionalStreaming(StreamObserver<HelloReply> responseObserver) {
			return new StreamObserver<HelloRequest>() {
				@Override
				public void onNext(HelloRequest request) {
					HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + request.getName()).build();
					responseObserver.onNext(reply);
					responseObserver.onNext(reply);
					responseObserver.onNext(reply);
				}
				@Override
				public void onError(Throwable t) {
					// ...
				}
				@Override
				public void onCompleted() {
					responseObserver.onCompleted();
				}
			};
		}
	}
}

Client-Implementierung

DemoClient.java


class DemoClient {
    public static void main(String[] args) {
        // ...
    }

    String unary() {
        HelloRequest request = HelloRequest.newBuilder().setName("Tom").build();
        return blockingStub().sayHelloUnary(request).toString(); // message: "Hello Tom"
    }

    String serverStreaming() {
        HelloRequest request = HelloRequest.newBuilder().setName("Tom").build();
        Iterator<HelloReply> replies = blockingStub().sayHelloServerStreaming(request);
        List<HelloReply> response = new ArrayList<>();
        while (replies.hasNext()) {
            response.add(replies.next());
        }
        return response.toString(); // [message: "Hello Tom", message: "Hello Tom", message: "Hello Tom"]
    }

    String clientStreaming() throws Exception {
        HelloRequest request = HelloRequest.newBuilder().setName("Tom").build();
        CountDownLatch finishLatch = new CountDownLatch(1);
        List<HelloReply> response = new ArrayList<>();
        StreamObserver<HelloRequest> streamObserver = stub().sayHelloClientStreaming(new StreamObserver<HelloReply>() {
            @Override
            public void onNext(HelloReply reply) {
                response.add(reply);
            }
            @Override
            public void onError(Throwable t) {
                // ...
            }
            @Override
            public void onCompleted() {
                finishLatch.countDown();
            }
        });
        streamObserver.onNext(request);
        streamObserver.onNext(request);
        streamObserver.onNext(request);
        streamObserver.onCompleted();
        finishLatch.await(10, TimeUnit.SECONDS);
        return response.toString(); // message: "Hello [Tom, Tom, Tom]"
    }

    String bidirectionalStreaming() throws Exception {
        HelloRequest request = HelloRequest.newBuilder().setName("Tom").build();
        CountDownLatch finishLatch = new CountDownLatch(1);
        List<HelloReply> response = new ArrayList<>();
        StreamObserver<HelloRequest> streamObserver = stub().sayHelloBidirectionalStreaming(new StreamObserver<HelloReply>() {
            @Override
            public void onNext(HelloReply reply) {
                response.add(reply);
            }
            @Override
            public void onError(Throwable t) {
                // ...
            }
            @Override
            public void onCompleted() {
                finishLatch.countDown();
            }
        });
        streamObserver.onNext(request);
        streamObserver.onNext(request);
        streamObserver.onNext(request);
        streamObserver.onCompleted();
        finishLatch.await(10, TimeUnit.SECONDS);
        return response.toString(); // [message: "Hello Tom" , message: "Hello Tom" , message: "Hello Tom" , message: "Hello Tom" , message: "Hello Tom" , message: "Hello Tom" , message: "Hello Tom" , message: "Hello Tom" , message: "Hello Tom" ]
    }

    private GreeterGrpc.GreeterBlockingStub blockingStub() {
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 6565)
                .usePlaintext(true)
                .build();
        return GreeterGrpc.newBlockingStub(channel);
    }

    private GreeterGrpc.GreeterStub stub() {
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 6565)
                .usePlaintext(true)
                .build();
        return GreeterGrpc.newStub(channel);
    }
}

Da das Observer-Muster verwendet wird, kann es etwas verwirrend sein, wenn Sie nicht damit vertraut sind. Diese Art von Schnittstelle wird auch in RxJava und Reactor verwendet, um sie in jede zu integrieren Reactive-grpc usw. ist ebenfalls vorhanden.

Lastverteilung

Da die gRPC-Kommunikation eine bidirektionale Kommunikation ermöglicht, handelt es sich um eine permanente Verbindung über eine TCP-Verbindung. Dies ist effizienter als HTTP / 1.1, das eine Verbindung pro Kommunikation herstellt, jedoch einen ordnungsgemäßen Lastausgleich erfordert. Im Folgenden sind die Methoden und Funktionen aufgeführt.

Art Erläuterung verdienen デverdienen
Proxy Proxy Einfache clientseitige Implementierung
Auch für unzuverlässige Kunden zugänglich
Hohe Latenz
Abhängig vom Durchsatz der LB
Client Side Client Side Hohe Leistung, da keine mittlere Schicht vorhanden ist Die Client-Implementierung ist kompliziert und es ist erforderlich, einen Mechanismus für die Integritätsprüfung und Lastverteilung vorzubereiten.
Muss für jede Sprache implementiert werden
Ein Mechanismus zur Gewährleistung der Zuverlässigkeit des Kunden ist erforderlich

Client-Lastausgleich mit Eureka

Mit Eureka können Sie einen Client-Lastausgleich erzielen. Es gibt verschiedene Möglichkeiten, es zu implementieren, aber es ist praktisch, grpc-spring-boot-Starter zusammen mit der Implementierung von Service zu verwenden.

Spring Boot + Eureka Server-Implementierung

Fügen Sie auf der Serverseite wie bei der Verwendung von normalem Eureka Abhängigkeiten hinzu, legen Sie den Anwendungsnamen, den zu verwendenden Port und die Eureka-Servereinstellungen fest und aktivieren Sie Eureka.

build.gradle


//...

dependencies {
    // ...
	compile('org.springframework.cloud:spring-cloud-starter-eureka')
}

//...

bootstrap.yml


spring:
    application:
        name: demo-server //Einstellung des Anwendungsnamens

application.yml


grpc:
    port: 6565 //gRPC-Port-Einstellungen
eureka:
    instance:
        nonSecurePort: ${grpc.port} //Auf gRPC-Port und Eureka einstellen

DemoServerApplication.java


@SpringBootApplication
@EnableEurekaClient //Aktivieren Sie Eureka
class DemoServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoServerApplication.class, args);
	}
	
	// ...
}

Spring Boot + Eureka Client-Implementierung

Die Client-Implementierung ist einfach: Fügen Sie Abhängigkeiten hinzu, aktivieren Sie Eureka und verwenden Sie EurekaClient, um die IP und den Port des Servers abzurufen. Der Lastausgleich erfolgt auf der Eureka-Seite, sodass auf dem Client keine spezielle Implementierung erforderlich ist.

build.gradle


//...

dependencies {
    // ...
	compile('org.springframework.cloud:spring-cloud-starter-eureka')
}

//...

DemoClientApplication.java


@SpringBootApplication
@EnableEurekaClient //Aktivieren Sie Eureka
class DemoClientApplication {

    @Autowired
    EurekaClient client;

    void sayHello(HelloRequest request) {
        InstanceInfo instanceInfo = client.getNextServerFromEureka("backend-service", false); //Abrufen von Serverinformationen vom Eureka-Client
        ManagedChannel channel = ManagedChannelBuilder.forAddress(instanceInfo.getIPAddr(), instanceInfo.getPort()) //Stellen Sie IP und Port über Instanzinformationen ein
                .usePlaintext(true)
                .build();
                
        GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel);
        stub.sayHello(request);
    }
	
	// ...
}

Fehlerbehandlung

Fehlerbehandlung durch onError

Der Grundfehler verwendet die "onError" -Methode von "StreamObserver".

class DemoServer {
	@Override
	public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
		try {
		
			// ...	
			
		} catch (Exception e) {
	
			StatusRuntimeException exception = Status.INTERNAL
					.withDescription(e.getMessage())
					.withCause(e)
					.asRuntimeException();
			responseObserver.onError(exception);
		}
	}
}

DemoClient.java


class DemoClient {
    public static void main(String[] args) {        
        try {
		
			// ...	
	
        } catch (StatusRuntimeException e) {
        	e.printStackTrace(); // io.grpc.StatusRuntimeException: INTERNAL: error message...
        }
    }
}

Hinzufügen von Fehlerinformationen durch Metadaten

In der Produktion möchten Sie häufig detailliertere Informationen zu Fehlern verarbeiten.

Verwenden Sie in diesem Fall "Metadaten". Zu "Metadaten" können verschiedene Informationen hinzugefügt werden, es ist jedoch zweckmäßig, eine Protodatei zu verwenden.

message Error {
  string message = 1;
  string detail = 2;
}
class DemoServer {
	@Override
	public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
		try {
		
			// ...	
				
		} catch (Exception e) {
			Metadata metadata = new Metadata(); //Metadaten generieren
			Error error = Error.newBuilder()
					.setMessage("my error")
					.setDetail("error detail")
					.build();
			Metadata.Key<Error> key = ProtoUtils.keyForProto(error);
			metadata.put(key, error); //Fehlerinformationen hinzugefügt
	
			StatusRuntimeException exception = Status.INTERNAL
					.withDescription(e.getMessage())
					.withCause(e)
					.asRuntimeException(metadata); //Metadaten hinzufügen
			responseObserver.onError(exception);
		}
	}
}

DemoClient.java


class DemoClient {
    public static void main(String[] args) {        
        try {
		
			// ...	
				        
        } catch (StatusRuntimeException e) {
            e.printStackTrace();
            Status status = Status.fromThrowable(e); // Status{code=INTERNAL, description=Invalid parameter, cause=null}
            Metadata metadata = Status.trailersFromThrowable(e); // Metadata(content-type=application/grpc,helloworld.error-bin=CghteSBlcnJvchIMZXJyb3IgZGV0YWls)
            Error error = metadata.get(ProtoUtils.keyForProto(Error.getDefaultInstance())); // error=message: "my error detail: "error detail"
        }
    }
}

Transparente Fehlerbehandlung mit Interceptor

Wenn auf dem Server eine Ausnahme ohne Fehlerbehandlung aufgrund von onError auftritt, tritt auf der Clientseite eine StatusRuntimeException (Status = Unbekannt) auf. Es kann erkennen, dass auf der Serverseite eine Ausnahme aufgetreten ist, es ist jedoch nicht bekannt, welche Art von Ausnahme aufgetreten ist. Auf jedem serverseitigen Endpunkt ist ein Try-Catch erforderlich, da normale Anwendungen immer eine RuntimeException auslösen können. Eine solche Verarbeitung wird jedoch sehr redundant, sodass der Interceptor die Verarbeitung transparent einbettet. Sie können Ihre eigene Fehlerbehandlung implementieren, indem Sie "io.grpc.ServerInterceptor" implementieren. Es ist jedoch zweckmäßig, den Standard "io.grpc.util.TransmitStatusRuntimeExceptionInterceptor" zu verwenden, wenn Sie mit der allgemeinen Fehlerbehandlung zufrieden sind.

DemoServer.java


class DemoServer {

    public static void main(String[] args) throws Exception {

        Server server = ServerBuilder.forPort(6565)
                .intercept(TransmitStatusRuntimeExceptionInterceptor.instance())  //Interceptor-Registrierung
                .addService(new GreeterImpl())
                .build();

        // ...			
    }
    
    static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
        @Override
        public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
        
            //Generieren Sie Metadaten usw....

            //Wirf StatusRuntimeException anstelle von onError
            throw Status.INTERNAL
                .withDescription("server error")
                .asRuntimeException(metadata);
        }
    }
}

Wiederholen Sie die Verarbeitung

Wenn der Server aus irgendeinem Grund auf einen Fehler stößt, können Sie es auf der Clientseite erneut versuchen.

DemoClient.java


class DemoClient {
    public static void main(String[] args) {        

        //Wiederholen Sie die Richtlinieneinstellungen
        Map<String, Object> retryPolicy = new HashMap<>();
        retryPolicy.put("maxAttempts", 3D);
        retryPolicy.put("initialBackoff", "0.5s");
        retryPolicy.put("maxBackoff", "1s");
        retryPolicy.put("backoffMultiplier", 2D);
        retryPolicy.put("retryableStatusCodes", Arrays.asList("UNAVAILABLE"));
        Map<String, Object> methodConfig = new HashMap<>();
        Map<String, Object> name = new HashMap<>();
        name.put("service", "helloworld.Greeter");
        methodConfig.put("name", Collections.singletonList(name));
        methodConfig.put("retryPolicy", retryPolicy);
        Map<String, Object> serviceConfig = new HashMap<>();
        serviceConfig.put("methodConfig", Collections.singletonList(methodConfig));

        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 6565)
            .usePlaintext()
            .enableRetry()  //Wiederholungsversuch aktivieren
            .defaultServiceConfig(serviceConfig) //Einstellungen übernehmen
            .build();

        // ...

        //Wenn UNAVAILABLE auftritt, wird es automatisch gemäß der Wiederholungsrichtlinie wiederholt.
        HelloReply reply = stub.sayHello(request); 

        // ...
    }
}

Validierung

Es gibt keine spezifische Spezifikation für die gRPC-Validierung. Behandeln Sie sie daher wie einen normalen Fehler. Es gibt auch ein "Status.INVALID_ARGUMENT" für falsche Eingaben.

Sicherheit

gRPC bietet den Mechanismus der Authentifizierung mithilfe von SSL / TLS und der Authentifizierung mithilfe von Google-Token.

Autorisierung durch Spring Security

Möglicherweise möchten Sie eine Funktion wie "PreAuthorize" in Spring Security. Derzeit wird eine solche Funktion nicht offiziell unterstützt, sie kann jedoch mithilfe eines Interceptors implementiert werden. Die folgenden Einträge sind hilfreich.

DemoGrpcService.java


@GRpcService
public class DemoGrpcService extends DemoServiceGrpc.DemoServiceImplBase {

    @Override
    @PreAuthorize("hasRole('USER')")
    public void list(ListRequest request, StreamObserver<ListResponse> responseObserver) {
        // ...
    }

    @Override
    @PreAuthorize("hasRole('ADMIN')")
    public void buy(BuyRequest request, StreamObserver<BuyResponse> responseObserver) {
        // ...
    }
}

Dies wird auch in Grpc-Spring-Boot-Starter-Problemen berücksichtigt.

Da gRPC derzeit verwendet wird, wird es häufig für die Kommunikation innerhalb der Plattform und nicht in einer weit geöffneten Umgebung verwendet. Daher ist es besser zu überlegen, wie viel Kontrolle erforderlich ist.

Prüfung

Verwenden Sie beim Testen von gRPC die offiziell bereitgestellte Bibliothek.

Servertest

build.gradle


//...

dependencies {
    // ...
	testCompile "io.grpc:grpc-testing:${grpcVersion}"
}

//...

DemoServerTest.java


class DemoServerTest {

	//Verwenden Sie die von der gRPC-Bibliothek bereitgestellte Regelklasse
    @Rule
    public GrpcServerRule grpcServerRule = new GrpcServerRule().directExecutor();

    @Test
    public void test() {
        grpcServerRule.getServiceRegistry().addService(new GreeterService());
        GreeterGrpc.GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(grpcServerRule.getChannel());

        String testName = "test name";
        HelloReply reply = blockingStub.sayHello(HelloRequest.newBuilder().setName(testName).build());

        assertEquals("Hello " + testName, reply.getMessage());
    }
}

Client-Tests

DemoClientTest.java


class DemoClientTest {

	//Verwenden Sie die von der gRPC-Bibliothek bereitgestellte Regelklasse
    @Rule
    public GrpcServerRule grpcServerRule = new GrpcServerRule().directExecutor();

    @Test
    public void test() {
        GreeterGrpc.GreeterImplBase serviceImpl = Mockito.spy(new GreeterGrpc.GreeterImplBase() {}); //Verspotten der Server-Implementierung
        grpcServerRule.getServiceRegistry().addService(serviceImpl);

        ArgumentCaptor<HelloRequest> requestCaptor = ArgumentCaptor.forClass(HelloRequest.class);
        String testName = "test name";

        DemoClient client = new DemoClient(grpcServerRule.getChannel());;
        client.hello(testName);

        Mockito.verify(serviceImpl)
                .sayHello(requestCaptor.capture(), Matchers.any()); //Ausführung erfassen
                
        assertEquals(testName, requestCaptor.getValue().getName());
    }
    
}

.proto Management

Um Protokollpuffer mit gRPC verwenden zu können, müssen Sie .proto-Dateien verwalten. Es gibt keine offiziell unterstützten Funktionen für diese Methode, daher hier zwei typische.

Dediziertes Repository

Die von Mercari usw. verwendete Methode besteht darin, ein dediziertes Repository für .proto zu erstellen. (Proto File Management) In diesem Fall ist es einfacher, durch Verknüpfen mit CI eine Bibliothek für jede Client-Sprache zu generieren. Sie können auch das Submodul von Git usw. verwenden, um die .proto-Datei auf der Clientseite zu importieren, ohne dies zu tun.

Bruderschaft im serverseitigen Repository

Bei der Verwaltung mit einem dedizierten Repository werden der Code auf der Serverseite und das Repository der .proto-Datei getrennt, aber ich denke, dass es oft einfacher ist, mit .proto auf der Serverseite umzugehen, die es implementiert. In diesem Fall können Sie protodep verwenden, um die .proto-Dateien in jedem Repository auf der Clientseite zu aggregieren. Dies ermöglicht die Verwaltung von .proto-Dateien in der Anwendung auf der Serverseite und die Verwendung der auf der Client-Seite erforderlichen .proto-Dateien.

Browserkompatibel

gRPC-Web Um gRPC über einen Browser zu verwenden, gibt es gRPC-Web. Derzeit werden Nginx und Envoy als Proxy verwendet, um die Kommunikation zwischen dem Browser und dem gRPC-Server zu realisieren.

REST API In vielen Fällen möchten Sie über die REST-API auf eine mit gRPC erstellte Anwendung zugreifen. In diesem Fall ist grpc-gateway praktisch. Dies wird ein wenig lang sein, daher werde ich es als einen weiteren Eintrag zusammenfassen.

Zusammenfassung

gRPC ist ein sehr effektiver Mechanismus zum Erstellen eines Systems über mehrere Anwendungen, aber ich denke, dass es im tatsächlichen Betrieb viele unklare Punkte gibt, deshalb habe ich es zusammengefasst. Außerdem werde ich diesen Eintrag aktualisieren, wenn ich mehr Wissen erhalte.

Referenz

Recommended Posts

Wichtige Punkte für die Einführung von gRPC in Java
[memo] RSA-Schlüsselpaar für SSH in Java generieren
Anfänger spielen Janken-Spiele in Java
[Für Anfänger] Führen Sie Selenium auf Java aus
Einstellungen für das SSL-Debugging in Java
Doppelte Karte sortiert nach Schlüssel in Java
Erste Schritte für tiefes Lernen in Java
Schlüssel vom Wert in Java Map umkehren
Tipps zur Behandlung von gRPC-Fehlern in Ruby
Idee, Symbole für OOP in das Flussdiagramm einzufügen
Für JAVA-Lernen (2018-03-16-01)
Techniken zum Lesen von Java-Quellcode in Eclipse
Partisierung in Java
Änderungen in Java 11
Lösung für NetBeans 8.2 funktioniert nicht in Java 9-Umgebung
Janken in Java
2017 IDE für Java
Einführung des NLP4J- [000] Natural Language Processing Index in Java
Stellen Sie mit vim die Popup-Anzeige für die Java-Sprache ein.
Umfangsrate in Java
Punkte, die beim Erstellen der VS Code- und Java-Entwicklungsumgebung hängen bleiben
Java für Anweisung
FizzBuzz in Java
Vergleichen Sie die PDF-Ausgabe in Java für Snapshot-Tests
Aktivieren / Deaktivieren von SNI in Java für jede Kommunikation
Punkte, die bei Java beachtet werden müssen, sind gleich
Dinge, auf die Sie bei der zukünftigen Java-Entwicklung achten sollten
Ein Hinweis zum Initialisieren von Feldern im Java-Lernprogramm
[Für Anfänger] Mindestbeispiel für die Anzeige von RecyclerView in Java
Abrufen von Gebietsschemaobjekten für alle in Java verfügbaren Gebietsschemas
Sortieren Sie die Map-Werte in aufsteigender Schlüsselreihenfolge in Java TreeMap
Dies und das zum Bearbeiten von ini in Java. : inieditor-java
[Java] Erläutert die ConcurrentModificationException, die in java.util.ArrayList für Neulinge auftritt
Ich habe versucht, die erweiterte for-Anweisung in Java zu verwenden
Erstellen Sie eine API-Schlüsselauthentifizierung für die Web-API in Spring Security
Lesen Sie JSON in Java
Interpreter-Implementierung durch Java
Machen Sie einen Blackjack mit Java
Janken App in Java
Einschränkungsprogrammierung in Java
Setzen Sie Java8 in Centos7
Verbinden Sie Arrays in Java
"Hallo Welt" in Java
Aufrufbare Schnittstelle in Java
[Java] Paket für die Verwaltung
[Java] für Anweisung / erweitert für Anweisung
Kommentare in der Java-Quelle
Azure funktioniert in Java
Formatieren Sie XML in Java
[Für Neulinge] Einführung von JUnit
Einfache HTML-Spezialchars in Java
Gegenmaßnahmen für OutOfMemoryError in Java
Boyer-Moore-Implementierung in Java
Hallo Welt in Java
Verwenden Sie OpenCV mit Java
WebApi-Memorandum mit Java
Typbestimmung in Java
Befehle in Java ausführen (Ping)
NLP für Java (NLP4J) (2)