🐡

Dartを使用したgRPCプログラムについて

2023/02/28に公開

初めに

FlutterでgRPCを利用したいと思い、Dartを使用してgRPCの動作確認をしました。Dartを使用したgRPCの情報は少ないと思うので、少しでも学習したことを読んでくれている人に還元できればと思い、この記事を書くことにしました。

gRPCとは

gRPCとは、サーバ側にある関数をクライアント側のアプリケーションから簡単に呼び指すことができます。また、サーバ側がGoでクライアントがDartなど異なる言語環境でも利用することができます。gRPCを活用することで、ネットワークを活用したサービスや分散アプリケーションを簡単に作成することができます。
 gRPCは動作により、Unary RPC,Server streaming RPC,Client streaming RPC,Bidirectional streaming RPCの4種類があります。

Unary RPC

クライアントから1つのリクエストに対してサーバから1つのレスポンスが返ってきます。
Unary RPC
Unary RPC

Server streaming RPC

クライアントから1つのリクエストに対してサーバから複数のレスポンスが返ってきます。
Server streaming RPC
Server streaming RPC

Client streaming RPC

クライアントから複数のリクエストに対してサーバから1つのレスポンスが返ってきます。
Client streaming RPC
Client streaming RPC

Bidirectional streaming RPC

クライアントから複数のリクエストに対してサーバから複数のレスポンスが返ってきます。
Bidirectional streaming RPC
Bidirectional streaming RPC

gRPCのプログラミング

Protocol bufferからのコード作成

protoファイルを作成して、呼び出す関数の定義、引数や戻り値を定義します。ファイルを作成後、ツールを使い、コードを生成します。

hello.proto
syntax = "proto3";

package Hello;

// The greeting service definition.
service Hello {
  // Sends a greeting
  rpc SayHelllo (callHello) returns (responseHello) {}
}

message callHello {
  string hello = 1;

}

// The response message containing the greetings
message responseHello {
  bool response = 1;
}

関数の引数にstreamをつけて宣言するとServer streaming RPC、戻り値にstreamをつけて宣言するとClient streaming RPC、引数、戻り値の両方にstreamをつけて宣言するとBidirectional streaming RPCとなります。
作成したprotoファイルをコマンドラインからコマンドを実行し、Dartのコードを生成します。

protoc -I gRPC/ gRPC/hello.proto --dart_out=grpc:gRPC

実行が完了するとgRPCのディレクトリにhello.pb.dart,hello.pbenum.dart,hello.pbgrpc.dart,hello.pbjson.dartの4つのファイルができます。

サーバ側の作成

サーバ側のプログラムを作成します。

main.dart
import 'package:grpc/grpc.dart';
import 'gRPC/hello.pb.dart';
import 'gRPC/hello.pbgrpc.dart';

class hello extends HelloServiceBase {
  
  Future<responseHello> sayHelllo(ServiceCall call, callHello request) async {
    print(request.hello);
    return responseHello()..response= true;
  }

}

Future<void> main() async {
  final server = Server([hello()]);
  await server.serve(port: 50051);
  print('Server listening on port ${server.port}...');
}

サーバ側ではクライアントから送られた文字列を受け取り、それを表示し、戻り値としてtrueを返すようにしています。

クライアント側の作成

クライアント側のプログラムを作成します。

main.dart
import 'package:grpc/grpc.dart';

import '../Server/gRPC/hello.pbgrpc.dart';

Future<void> main() async {
  // エンドポイントへのチャンネルを用意
  final channel = ClientChannel(
    'localhost',  // サーバの IP アドレス(またはホスト名)を指定すること
    port: 50051,
    options: const ChannelOptions(
      // TLS を無効にする
      credentials: ChannelCredentials.insecure(),
    ),
  );

  // クライアントの用意
  // (クライアントスタブと呼ばれる)
  final client = HelloClient(channel);

  // リクエストデータの用意
  final request = callHello();
  request.hello = "ssssss";

  // サーバが起動していないときなどの例外を捕捉
  try {
    // リクエストを sayHello() に渡してサーバに送ると
    // レスポンスが戻り値として返ってくる
    final response = await client.sayHelllo(request);

    // レスポンスから情報を取り出す
    print(response.response);

  } catch (e) {
    print('Caught error: $e');
  } finally {
    // 終わったらチャンネルをシャットダウン
    await channel.shutdown();
  }
}

サーバ側に文字列を送信して、サーバからの戻り値を表示しています。

最後に

紹介したサンプルはUnary gRPCを紹介しています。その他のものはgithubで公開しているのでご覧いただけたらと思います。綺麗コードではないですが、参考になればと思います。
https://github.com/Keisuke-Hongyo/Dart_gRPC/

Discussion