gRPCのデバッグにはevansを使うと便利

gRPCクライアントevans
gRPC client: evans

  

先日会社のエンジニアブログでgRPCに関する以下の記事を書いていた。

blog.engineer.adways.net

blog.engineer.adways.net

gRPCを使うメリットや実装方法に関することを書きまとめ、ある程度の知識も得られたと思っている。
ある程度gRPCに関する実装に慣れてきたところで、gRPCもHTTPのようにcurlデバッグできないのだろうか?と思っていたところevansというOSSを発見した。
今回はgRPCのデバッグツールevansに関することを軽くまとめる。

会社のブログに載せた個人のGithubリポジトリをそのまま流用してデバッグ手法を試みる。

github.com

このリポジトリではKotlinでSpring bootを使用し、gRPCサーバをポート6565で公開するように実装している。
言語やフレームワークは特にデバッグ時には関係ないので前提として知っておくのは起動しているポート番号が6565であるということだけでいい。

デバッグに必要なのはgRPCのプロトコル定義ファイルのみ。
さっき載せたリポジトリ内のプロトコル定義ファイルの内容は以下となる。

syntax = "proto3";

package taskstore;

option java_multiple_files = true;
option java_outer_classname = "TaskStore";
option java_package = "com.example.demo.taskstore";

service Taskstore {
  rpc GetTasks(GetTasksRequest) returns (GetTasksResponse) {}
  rpc AddTask(AddTaskRequest) returns (AddTaskResponse) {}
  rpc UpdateTask(UpdateTaskRequest) returns (UpdateTaskResponse) {}
  rpc DeleteTask(DeleteTaskRequest) returns (DeleteTaskResponse) {}
}

message GetTasksRequest {
}
message GetTasksResponse {
  repeated Task task = 1;
}

message AddTaskRequest {
  string content = 1;
}
message AddTaskResponse {
  Task task = 1;
}

message UpdateTaskRequest {
  int64 id = 1;
  bool done = 2;
}
message UpdateTaskResponse {
  Task task = 1;
}

message DeleteTaskRequest {
  int64 id = 1;
}
message DeleteTaskResponse {
  bool success = 1;
}

message Task {
  int64 id = 1;
  string content = 2;
  bool done = 3;
}

evansのインストール

以下のリポジトリがevansというOSS
curlのようにサーバにリクエストを送ることもできるし、プロトコル定義をテーブル形式で表示したりと表現力豊かなgRPCクライアント。

github.com

$ brew tap ktr0731/evans
$ brew install evans

evansでgRPCのデバッグ

以降はgRPCサーバが立ち上がっていることを前提として書く。
念のため再度言及するが、今回の僕のリポジトリではSpring bootでポート6565でgRPCサーバが起動している。

evansのhelp表示が以下。

$ evans --help

evans 0.2.8
Usage: evans [--interactive] [--editconfig] [--host HOST] [--port PORT] [--package PACKAGE] [--service SERVICE] [--call CALL] [--file FILE] [--path PATH] [--header HEADER] [PROTO [PROTO ...]]

Positional arguments:
  PROTO                  .proto files

Options:
  --interactive, -i      use interactive mode
  --editconfig, -e       edit config file by $EDITOR
  --host HOST, -h HOST   gRPC host
  --port PORT, -p PORT   gRPC port [default: 50051]
  --package PACKAGE      default package
  --service SERVICE      default service. evans parse package from this if --package is nothing.
  --call CALL, -c CALL   call specified RPC
  --file FILE, -f FILE   the script file which will be executed (used only command-line mode)
  --path PATH            proto file path
  --header HEADER        headers set to each requests
  --help, -h             display this help and exit
  --version              display version and exit

--portでターゲットとなるgRPCサーバのポートを指定し、プロトコル定義ファイルの場所を教えてあげる。

$ evans --port 6565 proto/task.proto

  ______
 |  ____|
 | |__    __   __   __ _   _ __    ___
 |  __|   \ \ / /  / _. | | '_ \  / __|
 | |____   \ V /  | (_| | | | | | \__ \
 |______|   \_/    \__,_| |_| |_| |___/

 more expressive universal gRPC client


127.0.0.1:6565> 

あまりコマンドも多くなくシンプルなのため非常に使いやすい。
コマンドの一覧表を提示しておく。

コマンド名 意味
show package packageの一覧表示
show service 選択したpackageに存在するserviceを一覧表示
show message 選択したpackageに存在するmessageを一覧表示
show rpc 選択したservice内に存在するrpcメソッドを一覧表示
desc message名 引数に与えたmessageのパラメータ名と型を表示
package package名 引数に与えたpackageを選択する
service service名 引数に与えたserviceを選択する
call rpc名 引数に与えたrpcメソッドをコールする

一応解説する。
packageを選択しないと、serviceやmessageは一覧表示できない。
serviceを選択しないと、rpcメソッドの一覧は表示できないしcallもできない。
これは当たり前だがpackageは複数存在し得るし、ひとつのpackage内に複数のserviceが存在し得るから当然のこと。
なお、callコマンドは対話型でリクエストの引数を与えることができる。
早速試してみる。

127.0.0.1:6565> show package
+-----------+
|  PACKAGE  |
+-----------+
| taskstore |
+-----------+

127.0.0.1:6565> package taskstore

taskstore@127.0.0.1:6565> show service
+-----------+------------+-------------------+--------------------+
|  SERVICE  |    RPC     |    REQUESTTYPE    |    RESPONSETYPE    |
+-----------+------------+-------------------+--------------------+
| Taskstore | GetTasks   | GetTasksRequest   | GetTasksResponse   |
|           | AddTask    | AddTaskRequest    | AddTaskResponse    |
|           | UpdateTask | UpdateTaskRequest | UpdateTaskResponse |
|           | DeleteTask | DeleteTaskRequest | DeleteTaskResponse |
+-----------+------------+-------------------+--------------------+

taskstore@127.0.0.1:6565> show message
+--------------------+
|      MESSAGE       |
+--------------------+
| GetTasksRequest    |
| GetTasksResponse   |
| AddTaskRequest     |
| AddTaskResponse    |
| UpdateTaskRequest  |
| UpdateTaskResponse |
| DeleteTaskRequest  |
| DeleteTaskResponse |
| Task               |
+--------------------+

taskstore@127.0.0.1:6565> service Taskstore

taskstore.Taskstore@127.0.0.1:6565> show rpc
+------------+-------------------+--------------------+
|    RPC     |    REQUESTTYPE    |    RESPONSETYPE    |
+------------+-------------------+--------------------+
| GetTasks   | GetTasksRequest   | GetTasksResponse   |
| AddTask    | AddTaskRequest    | AddTaskResponse    |
| UpdateTask | UpdateTaskRequest | UpdateTaskResponse |
| DeleteTask | DeleteTaskRequest | DeleteTaskResponse |
+------------+-------------------+--------------------+

taskstore.Taskstore@127.0.0.1:6565> call GetTasks
{
  "task": [
    {
      "id": 65,
      "content": "Hello"
    }
  ]
}

taskstore.Taskstore@127.0.0.1:6565> 

最後のcallコマンドでGetTasksというrpcメソッドをコールしたらしっかりjson形式ではあるが、レスポンスが確認できた。

evansはコマンドライン一発で完結できるコマンドラインモードと今回説明したインタラクティブモードというものがある。
コマンドラインモードではgRPCサーバに対して、とあるスクリプトを定期実行したい時などに使えそうだ。
インタラクティブモードはやはりgRPC APIを手動で動作確認したい場合に頻繁に使えそう。