• トップ
  • ブログ一覧
  • Evansを使ったGolang × gRPC開発
  • Evansを使ったGolang × gRPC開発

    広告メディア事業部広告メディア事業部
    2022.10.04

    IT技術

    gRPCを用いたAPIサーバー開発

    gRPCはgoogle社が開発したRPC通信のひとつで、マイクロサービス間での通信や速度が求められる場合によく使用されます。

    gRPCではデータ転送のシリアライズフォーマットとしてProtocol Buffersを採用しており、protoファイルというIDLを記述しコンパイルすると、任意の言語のクライアント・サーバー用のコードを自動で生成してくれる優れものです。

    またスキーマファーストの開発となるため、API仕様書が無くてもprotoファイルを見れば分かるといった開発ができることもメリットの1つとなります。

    しかしREST APIであれば動作確認する場合にCurlやPostmanなどを使って簡単に動作確認ができますが、gRPCの場合はバイナリベースのため人間が目視して確認するには専用のクライアントツールを使う必要があります。

    そこで本記事ではサンプルで作った簡単なgRPC APIサーバーに対して、EvansというgRPCクライアントツールを使って動作確認をする方法を説明していこうと思います。

    Evansとは

    改めてEvansとはgRPC向けサードパーティクライアントツールになります。

    https://github.com/ktr0731/evans

    他にもgRPC公式のgrpc_cliや特に有名なgRPCurlなどがあります。

    その中でもEvansは他と比べて特徴的なのが、REPLモードとCLIモードの2つを提供していることが挙げられます。

    REPLモード

    最大の特徴であるREPLモードは他のクライアントツールにはない、インタラクティブなUIでサーバーとやりとりをすることが出来ます。

    参考: 公式Github

    使い方は後ほど説明しますが、実際に使ってみると非常に使いやすいです。

    予測補完機能が優秀で次に何を入力すればいいか、選択肢は何があるかなどを画面上に従って入力するだけで使用することができます。

    そのためAPI名なんだっけ...といったことはなく使い慣れていない場合でもサクサクと動作確認をすることができます。

    CLIモード

    CLIモードは他のgrpc_cliやgRPCurlと同じような、非インタラクティブなUIによる操作モードになります。

    参考: 公式Github

    使い方としてはコマンドラインからサービスやAPI名を入力してリクエストを送るとJSON形式でレスポンスを取得することができます。

    ある程度慣れていればこちらの方が素速く使うことができますし、レスポンスがJSONで手に入るため他のコマンドラインを組み合わせることで様々な加工がしやすくCLIモードを使うことも非常に多いかと思います。

    今回使うAPIサーバーのサンプル

    ここからは簡単なgRPCのAPIサーバーを作って、実際にEvansを使って動作確認をしてみたいと思います。

    従業員取得API

    今回は従業員(Employee)のデータを返すAPIを作成しました。gRPCの開発は本記事の主旨ではないためどのようなAPIを作ったのかだけ簡単に説明いたします。

    protoファイルは以下の通りです。

    1syntax = "proto3";
    2package proto.employee;
    3
    4option go_package = "gen/api";
    5
    6service EmployeeService {
    7  rpc Employee (EmployeeRequest) returns (EmployeeResponse);
    8  rpc Employees (EmployeesRequest) returns (EmployeesResponse);
    9  rpc EmployeesByType (EmployeesByTypeRequest) returns (EmployeesByTypeResponse);
    10}
    11
    12enum EmployeeType {
    13  FullTime = 0;
    14  PartTime = 1;
    15}
    16
    17message Employee {
    18  int64 id = 1;
    19  string name = 2;
    20  int32 age = 3;
    21  EmployeeType type = 4;
    22}
    23
    24message EmployeeRequest {
    25  int64 id = 1;
    26}
    27
    28message EmployeeResponse {
    29  Employee employee = 1;
    30}
    31
    32message EmployeesRequest {}
    33
    34message EmployeesResponse {
    35  repeated Employee employees = 1;
    36}
    37
    38message EmployeesByTypeRequest {
    39  EmployeeType employeeType = 1;
    40}
    41
    42message EmployeesByTypeResponse {
    43  repeated Employee employees = 1;
    44}

    また従業員データはダミーデータとして以下のようにべた書きしています。

    1func employeeList() []*api.Employee {
    2	return []*api.Employee{
    3		{
    4			Id:   1,
    5			Name: "Yamada",
    6			Age:  20,
    7			Type: api.EmployeeType_FullTime,
    8		},
    9		{
    10			Id:   2,
    11			Name: "Suzuki",
    12			Age:  25,
    13			Type: api.EmployeeType_PartTime,
    14		},
    15		{
    16			Id:   3,
    17			Name: "Sasaki",
    18			Age:  45,
    19			Type: api.EmployeeType_FullTime,
    20		},
    21	}
    22}

    Employee (従業員情報)

    従業員情報にはID、名前、年齢、雇用形態の4つの情報を持ちます。

    雇用形態はEnum型で正社員とパートの2パターンあります。

    1message Employee {
    2  int64 id = 1;
    3  string name = 2;
    4  int32 age = 3;
    5  EmployeeType type = 4;
    6}
    7
    8enum EmployeeType {
    9  FullTime = 0;
    10  PartTime = 1;
    11}

    ServiceとRPCメソッド

    従業員情報に関して1件取得、全件取得、雇用形態別に取得の3種類となります。

    1service EmployeeService {
    2  rpc Employee (EmployeeRequest) returns (EmployeeResponse);
    3  rpc Employees (EmployeesRequest) returns (EmployeesResponse);
    4  rpc EmployeesByType (EmployeesByTypeRequest) returns (EmployeesByTypeResponse);
    5}

    Evansを使ってAPIコールする

    では早速Evansを使って実際に動作確認をやってみようと思います。

    Evansのインストール

    まずはEvansをHomebrewを使ってインストールします。

    1brew tap ktr0731/evans
    2brew install evans

    バージョン確認をして表示されればインストール完了となります。

    1$ evans -v             
    2evans 0.10.9

     

    また本記事ではEvansバージョン0.10.9を使っていきます。

    コマンドとオプション

    Evansに準備されている代表的なコマンドとオプションの一覧です。

    コマンド説明
    replREPLモードで実行する
    cliCLIモードで実行する
    オプション説明
    --protoprotoファイルのパスを指定する
    --hostgRPCサーバーのホストを指定
    --port, -pgRPCサーバーのポートを指定 (default "50051")
    --reflection, -rgRPC reflectionを使用している場合 (default "false")

    gRPC リフレクションについて

    基本的には--protoオプションを使って定義元となるProtocol Buffersファイルを指定する必要があるのですが、実際の開発現場では大量のファイルが作られることがほとんどです。

    そうなってくると毎回個別に指定するのは非常に面倒になります。

    そこでgRPCの公式が用意しているリフレクションの設定をサーバー側で登録していれば、毎回Protocol BuffersのIDLを直接的に読み込まずにメソッドを呼び出すことが出来るようになります。

    https://pkg.go.dev/google.golang.org/grpc/reflection

    今回もサーバー起動時にリフレクションの登録をしているためprotoの指定はせずに -r オプションを指定して実行していきます。

    REPLモード

    まずはREPLモードを使って動作確認してみます。

    REPLモードとリフレクションオプションを指定して実行すると、このようにインタラクティブモードで立ち上がります。

    1$ evans -r repl 
    2  ______
    3 |  ____|
    4 | |__    __   __   __ _   _ __    ___
    5 |  __|   \ \ / /  / _. | | '_ \  / __|
    6 | |____   \ V /  | (_| | | | | | \__ \
    7 |______|   \_/    \__,_| |_| |_| |___/
    8
    9 more expressive universal gRPC client
    10
    11proto.employee.EmployeeService@127.0.0.1:50051>

    RPCの実行をするためにはpackageとserviceを指定し、最後に実行するRPCメソッドを選びます。

    1proto.employee.EmployeeService@127.0.0.1:50051> package proto.employee
    2
    3proto.employee@127.0.0.1:50051> service EmployeeService
    4
    5proto.employee.EmployeeService@127.0.0.1:50051> call Employee

    途中はこのように入力の補完候補が出てくるため覚えたりコピペしてくる必要さえありません。

    最後にrequestの引数である従業員IDを入力して実行すると

    1proto.employee.EmployeeService@127.0.0.1:50051> call Employee
    2id (TYPE_INT64) => 1
    3{
    4  "employee": {
    5    "age": 20,
    6    "id": "1",
    7    "name": "Yamada"
    8  }
    9}

    このように従業員情報を取得することができました。

    もし引数がEnum型の場合は選択肢が出てきます。

    1proto.employee.EmployeeService@127.0.0.1:50051> call EmployeesByType
    2Use the arrow keys to navigate: ↓ ↑ → ←
    3? employeeType (TYPE_ENUM) =>
    4FullTime
    5    PartTime

    他にもshowコマンドやdescコマンドで一覧や詳細情報を表示することも可能です。

    1proto.employee.EmployeeService@127.0.0.1:50051> show message
    2+-------------------------+
    3|         MESSAGE         |
    4+-------------------------+
    5| EmployeeRequest         |
    6| EmployeeResponse        |
    7| EmployeesByTypeRequest  |
    8| EmployeesByTypeResponse |
    9| EmployeesRequest        |
    10| EmployeesResponse       |
    11+-------------------------+
    12
    13proto.employee.EmployeeService@127.0.0.1:50051> desc EmployeeRequest
    14+-------+------------+----------+
    15| FIELD |    TYPE    | REPEATED |
    16+-------+------------+----------+
    17| id    | TYPE_INT64 | false    |
    18+-------+------------+----------+

    CLIモード

    今度は同じようにCLIモードで実行していきます。

    CLIの場合はevans cli [package].[service].[rpc method]のフォーマットで実行するメソッドを指定します。

    例えば全従業員情報を取得する場合はこのようになります。

    1echo '{}' | evans -r cli proto.employee.EmployeeService.Employees
    2{
    3  "employees": [
    4    {
    5      "age": 20,
    6      "id": "1",
    7      "name": "Yamada"
    8    },
    9    {
    10      "age": 25,
    11      "id": "2",
    12      "name": "Suzuki",
    13      "type": "PartTime"
    14    },
    15    {
    16      "age": 45,
    17      "id": "3",
    18      "name": "Sasaki"
    19    }
    20  ]
    21}

    もし引数が必要なAPIの場合はechoとパイプを使って渡すことが出来ます。

    1echo '{"id": 1}' | evans -r cli proto.employee.EmployeeService.Employee
    2{
    3  "employee": {
    4    "age": 20,
    5    "id": "1",
    6    "name": "Yamada"
    7  }
    8}

    これでモード別の動作確認は以上となります。

    さいごに

    今回はEvansというgRPCクライアントツールを使ってgRPCのAPIを動かしてみることができました。

    REPLモードとCLIモードを使ってみた感想としては、どちらを使うかは一長一短あるためケースバイケースかなと思いました。

    例えばPRを出してレビュアーに動作確認をして欲しい場合は、CLIモード用にコマンドを準備してあげた方がより早く確認することができるかと思いますし、自分で開発中に動かすのであればREPLモードの方が柔軟で楽に確認ができるため状況に応じて使い分けたらいいかと思います。

    ぜひEvansを使ってgRPC開発を楽しんでください!

    広告メディア事業部

    広告メディア事業部

    おすすめ記事