GrpcをC#で実装するには

GrpcをC#で実装するには

必要なNuGetパッケージ
Google.Protobuf
→やれることは以下のURLを参照
  https://kagasu.hatenablog.com/entry/2018/04/29/233407#1-NuGet%E3%81%8B%E3%82%89-GoogleProtobuf-%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B
  今回は作成したprotoファイルを使うことに使用

Grpc
→Grpcを使用するためのライブラリ

Grpc.Reflection
Grpc.Tools
→Grpcで使用するファイル等を自動生成してくれるライブラリ
  ビルドを実行することで自動生成される

Grpc環境の作成については以下のURLを参照
https://qiita.com/muroon/items/4e12dde47b9e8b1e94d3

前準備が完了したら、Grpcの実装を行っていく。

1.protoファイルの作成
  下記が書き方の一例

    syntax = "proto3";

    package Grpctst;

    service GrpcTest{
        rpc TestRun (TestRequest) returns (TestResponse) {}
    }

    message TestRequest{
        string request = 1; 
    }

    message TestResponse {
        string response = 1;
    }
    
    上記のファイルの意味
    syntax：決まり文句みたいなもの
    package:対象のnamespaceみたいなもの
    service サービス名：サービスの名前とメソッドの定義引数と戻りを定義できる。
    message メッセージ名:引数や戻り値の情報を定義する箇所
            stringやintなどの型を定義する。
            1は割り当てているIDみたいなものである。
            複数記載することも可能。

2.protoファイルの作成が完了後は、クラスライブラリMicrosoft.NET.Sdkを作成する。
  作成後はプロジェクトのソリューションの設定に以下を追加する。
  <ItemGroup>
    <Protobuf Include="protos/TestRun.proto" Link="TestRun.proto" />
  </ItemGroup>
  
3.追加後にビルドを実行する。
  実行するとクラスファイルを自動生成してくれる。
  Grpctst.cs
  GrpctstGrpc.cs
  が生成されていればOK(ファイル名はパッケージ名いよって異なる)
  これでGrpc操作を行う環境設定については完了。
  
4.データ受信の方法
  受信を行うために以下のファイルを作成する。
  GrpcTestImpl.cs(protoファイルのメソッドを設定するクラス)
  GrpcTestReceive.cs(Grpcとやりとりをするためのクラス)
  
  GrpcTestImpl.cs

  class GrpcTestImpl : GrpcTest.GrpcTestBase
  {
    public override Task<TestResponse> TestRun(TestRequest request, ServerCallContext context)
    {
      return Task.FromResult(new TestResponse { Response = request.request });
    }
  }
  
  上記はGrpcTest.GrpcTestBaseを継承しています。
  GrpcTestBaseは自動生成されたGrpctstGrpc.csの中で実装されています。
  TestRunメソッドをオーバーライドして処理を記載することができます。
  今回はテストですので、受信してきたデータをreturnするだけの処理としています。
  
  
  GrpcTestReceive.cs（ほとんど決まり文句）

    public class GrpcTestReceive
    {
      const int Port = 50051;
      
      public void Run()
      {
        Server server = new Server
        {
          Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) }
        };

      var grpcTest = GrpcTest
          .BindService(new GrpcTestImpl())
          .Intercept(new GrpcInterceptor());
      server.Services.Add(grpcTest);

      var reflectionService = ServerReflection
          .BindService(new ReflectionServiceImpl(GrpcTest.Descriptor))
          .Intercept(new GrpcInterceptor());
      server.Services.Add(reflectionService);

      server.Start();
    }
    
    public class GrpcInterceptor : Interceptor
    {
      public override Task<TResponse> ClientStreamingServerHandler<TRequest, TResponse>(
        IAsyncStreamReader<TRequest> requestStream
        , ServerCallContext context
        , ClientStreamingServerMethod<TRequest, TResponse> continuation
        )
      {
        return base.ClientStreamingServerHandler(requestStream, context, continuation);
      }

     public override Task DuplexStreamingServerHandler<TRequest, TResponse>(
        IAsyncStreamReader<TRequest> requestStream
        , IServerStreamWriter<TResponse> responseStream
        , ServerCallContext context
        , DuplexStreamingServerMethod<TRequest, TResponse> continuation
        )
      {
        return base.DuplexStreamingServerHandler(requestStream, responseStream, context, continuation);
      }

      public override Task ServerStreamingServerHandler<TRequest, TResponse>(
        TRequest request
        , IServerStreamWriter<TResponse> responseStream
        , ServerCallContext context
        , ServerStreamingServerMethod<TRequest, TResponse> continuation
        )
      {
        return base.ServerStreamingServerHandler(request, responseStream, context, continuation);
      }

      public override Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request
        , ServerCallContext context
        , UnaryServerMethod<TRequest, TResponse> continuation
        )
      {
        foreach (var header in context.RequestHeaders)
        {
          Console.WriteLine(header);
        }

        var tokenHeader = context.RequestHeaders.FirstOrDefault((entry) => entry.Key == "token");
        if (tokenHeader == null)
        {
          context.Status = new Status(StatusCode.Unauthenticated, "ヘッダーにトークン情報がありません.");
        }
        return base.UnaryServerHandler(request, context, continuation);
      }
    }
  

5.各種クラスの設定が完了したら実装は完了です。
  実装が完了したらテストを行ってみる。
  テストを行うのに便利なcurlツールでgrpcurlというのがあるので、使用してみるといいかもしれません。
  インストールから使い方については以下を参照。
  https://qiita.com/yukina-ge/items/a84693f01f3f0edba482
  
  grpccurlのコマンド一例
  サービス一覧表示
  grpcurl -plaintext localhost:50051 list 

  サービス内のメソッド一覧を表示
  grpcurl -plaintext localhost:50051 list サービス一覧表示で取得した名称

  サービス呼び出し
  grpcurl -plaintext localhost:50051 サービス一覧表示で取得した名称/サービス内のメソッド名称