Tonicによるrust gRPCサーバー開発

Tonicを用いてgRPC開発する際、サーバープロセスの構築以外に、protobufを適切に組込むことが必要になります。
Tonicの場合は、 PROST!を利用しており、protocコマンドで生成するフローとやや異なります。

なお、本記事のロジックと同等のコード生成CLIツール、 Alainをオープンソースで配布しています。

protoインクルードの構造

gRPC開発では、protobufの定義と各言語の変数との対応を意識することが重要です。
以下のようなprotobufのコードを組込む例を考えます。

syntax = "proto3";

package example.duck;

service ExampleDuck {
  rpc LameDuck(Dummy) returns (Dummy) {}
}

message Dummy {}

まず、マクロを用いてprotoをインクルードします。

pub mod example {
  pub mod duck {
        tonic::include_proto!("example.duck");
  }
}

この際、modの階層をprotobufのpackage名前空間に揃えて定義することが重要です。
構造を間違えている場合、 superがネストし過ぎるコードが出力されるといった非常に分かりにくいエラーに直面します。

proto定義の参照

インクルードしたproto定義は、mod構造に沿ってrustプロジェクトの名前空間にアサインされます。
先ほどの例では、crate::example::duck::Dummyのような名前でインポートされます。
なお、rpcはcrate::example::duck::lame_duck()のようにスネークケースになります。

この他、サーバーなどの構造体もツールの命名規約に沿って自動生成されています。target/ディレクトリに自動生成されたファイルがあり、個別のケースはコードを確認することになります。

  • example::duck::LameDuckServer
  • example::duck::LameDuckClient
  • example::duck::LameDuckService

サーバー実装では、protoに定義したrpcをすべて実装しないとコンパイル時エラーになります。

RequestとResponseの構造

Requestは、tonic::Request<example::duck::Dummy>のようにprotobufメッセージをtonic::Requestでラップしたものです。
メッセージはinto_inner()で取り出せます。

またResponseは、Result<tonic::Response<example::duck::Dummy, tonic::Status>のように、tonic::Responseまたはtonic::Statusを返すResult型です。

ビルドスクリプト

tonicは、cargo build実行時にprotobufをrustコードにコンパイルするフローを想定しています。
そのため、以下のようなbuild.rsをプロジェクトルートに置きます。

fn main() -> Result<(), Box<dyn std::error::Error>> {
    tonic_build::compile_protos("proto/example_duck.proto")?;
    Ok(())
}

なお、protobufコンパイルにはtonic-buildクレートが必要であるため、Cargo.tomlのbuild-dependenciesに追加しておきます。

[build-dependencies]
tonic-build	=  { version = "0.4", features = ["prost"] }

複数のserviceを提供するServer

gRPCではservicerpcをまとめる単位となり、proto内に複数のserviceを定義できます。

tonicはserviceを同名のトレイトに対応させています。
各トレイトは分割実装できないためservice内のrpcの数が増えると、長大なトレイト定義をimplすることになります。

serviceを論理的に分割できる場合には、各トレイト実装を別のファイルに定義できます。
各サービスはエントリポイントのServer構築プロセスで、 add_service()を用いて追加します。

add_service()メソッドは単一のサーバーに複数指定できるため、単一ポートに複数のサービスを統合できます。

中馬崇尋
Chuma Takahiro