スクラッチからのJavagRPC

公開: 2021-07-16

JavaでgRPCを実装する方法を見てみましょう。

gRPC(Googleリモートプロシージャコール):gRPCは、マイクロサービス間の高速通信を可能にするためにGoogleによって開発されたオープンソースのRPCアーキテクチャです。 gRPCを使用すると、開発者はさまざまな言語で記述されたサービスを統合できます。 gRPCは、Protobufメッセージング形式(Protocol Buffers)を使用します。これは、構造化データをシリアル化するための非常に効率的で高度にパックされたメッセージング形式です。

一部のユースケースでは、gRPCAPIがRESTAPIよりも効率的である場合があります。

gRPCでサーバーを書いてみましょう。 まず、サービスとモデル(DTO)を記述するいくつかの.protoファイルを作成する必要があります。 単純なサーバーの場合、ProfileServiceとProfileDescriptorを使用します。

ProfileServiceは次のようになります。

 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は、さまざまなクライアント/サーバー通信オプションをサポートしています。 それらをすべて分解します。

  • 通常のサーバー呼び出し–要求/応答。
  • クライアントからサーバーへのストリーミング。
  • サーバーからクライアントへのストリーミング。
  • そしてもちろん、双方向ストリーム。

ProfileServiceサービスは、インポートセクションで指定されたProfileDescriptorを使用します。

 syntax = "proto3"; package com.deft.grpc; message ProfileDescriptor { int64 profile_id = 1; string name = 2; }
  • int64はJavaの場合はLongです。 プロファイルIDを所属させます。
  • 文字列– Javaと同様に、これは文字列変数です。

GradleまたはMavenを使用してプロジェクトをビルドできます。 Mavenを使用する方が便利です。 さらに、Mavenを使用したコードもあります。 Gradleの場合、.protoの将来の世代はわずかに異なり、ビルドファイルは別の方法で構成する必要があるため、これは十分に重要です。 単純なgRPCサーバーを作成するには、依存関係が1つだけ必要です。

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

それはただ信じられないほどです。 このスターターは私たちのために途方もない量の仕事をします。

作成するプロジェクトは次のようになります。

Spring Bootアプリケーションを起動するには、GrpcServerApplicationが必要です。 そして、 .protoは、 .protoサービスのメソッドを実装します。 protocを使用し、書き込まれた.protoファイルからクラスを生成するには、protobuf-maven-pluginをpom.xmlに追加します。 ビルドセクションは次のようになります。

 <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 –.protoファイルが配置されているディレクトリを指定します。
  • outputDirectory –ファイルが生成されるディレクトリを選択します。
  • clearOutputDirectory –生成されたファイルをクリアしないことを示すフラグ。

この段階で、プロジェクトをビルドできます。 次に、出力ディレクトリで指定したフォルダに移動する必要があります。 生成されたファイルはそこにあります。 これで、 GrpcProfileServiceを徐々に実装できます。

クラス宣言は次のようになります。

 @GRpcService public class GrpcProfileService extends ProfileServiceGrpc.ProfileServiceImplBase

GRpcServiceアノテーション–クラスをgrpc-serviceBeanとしてマークします。

ProfileServiceGrpcProfileServiceImplBaseからサービスを継承するため、親クラスのメソッドをオーバーライドできます。 オーバーライドする最初のメソッドは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(); }

クライアントに応答するには、渡されたStreamObserverでonNextメソッドを呼び出す必要があります。 応答を送信した後、サーバーがonCompletedの作業を終了したことを示すシグナルをクライアントに送信します。 getCurrentProfileサーバーにリクエストを送信すると、応答は次のようになります。

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

次に、サーバーストリームを見てみましょう。 このメッセージングアプローチでは、クライアントはサーバーに要求を送信し、サーバーはメッセージのストリームでクライアントに応答します。 たとえば、ループで5つのリクエストを送信します。 送信が完了すると、サーバーはストリームが正常に完了したことを示すメッセージをクライアントに送信します。

オーバーライドされたサーバーストリームメソッドは次のようになります。

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

したがって、クライアントは、応答番号に等しいProfileIdを持つ5つのメッセージを受信します。

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

クライアントストリームはサーバーストリームと非常によく似ています。 今だけ、クライアントはメッセージのストリームを送信し、サーバーはそれらを処理します。 サーバーはメッセージをすぐに処理することも、クライアントからのすべての要求を待ってから処理することもできます。

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

クライアントストリームでは、サーバーがメッセージを受信するクライアントにStreamObserverを返す必要があります。 ストリームでエラーが発生した場合、onErrorメソッドが呼び出されます。 たとえば、正しく終了しませんでした。

双方向ストリームを実装するには、サーバーとクライアントからのストリームの作成を組み合わせる必要があります。

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

この例では、クライアントのメッセージに応答して、サーバーはpointCountが増加したプロファイルを返します

結論

gRPCを使用したクライアントとサーバー間のメッセージングの基本オプション(実装されたサーバーストリーム、クライアントストリーム、双方向ストリーム)について説明しました。

記事はSergeyGolitsynによって書かれました