gRPCクライアントのいろいろ

# はじめに

「gRPCサーバーを建てたけど、クライアントからどうやってリクエストするのがいいの？」

という人向けに、gRPCクライアントをいろいろ試した結果をまとめた。

# TL;DR

gRPCクライアントはいろいろあるので、以下のように用途に合わせて使う。

- とにかく一番簡単な方法でリクエストする -> [grpcc](https://github.com/njpatel/grpcc)
- ブラウザからgRPCライクにリクエストする -> [gRPC Web](https://github.com/improbable-eng/grpc-web)
- RestAPIライクにリクエストする -> [Google Cloud Endpoints](https://cloud.google.com/endpoints/docs/grpc?hl=ja) または [gRPC Gateway](https://github.com/grpc-ecosystem/grpc-gateway)

# 前提

- gRPCサーバーはlocalhostの6565ポートでアクセス可能とする
- 以下のようなprotoファイルで定義されたサーバーにアクセスする

```proto:person.proto

syntax = "proto3";

service Person {
    rpc GetPersons ( Empty ) returns ( PersonsModel ) {}
}

message Empty {}

message PersonModel {
    string name = 1;
    int32 id = 2;
}

message PersonsModel {
    repeated PersonModel person = 1;
}
```

# とにかく一番簡単な方法でリクエストする

[grpcc](https://github.com/njpatel/grpcc)を使う。

似たようなツールに[gRPC command line tool](https://github.com/grpc/grpc/blob/master/doc/command_line_tool.md)というのもあるが、ドキュメントを見た感じだとgrpccのほうが洗練されてそう。(gRPC command line toolも試したら追記する。)

## 特徴

- コード不要。インストールするだけですぐ使える。
- 開発時のテストとして使ったりするのに良い。
- 対話モードでは補完が効くのも素晴らしい。

## 使い方

対話モードで呼び出す

```sh
# protoファイルと接続先gRPCサーバーを指定し、対話モードで起動
$ grpcc --proto ./person.proto --address 127.0.0.1:6565 -i

# getPersonsを呼び出し
$ Person@127.0.0.1:6565> client.getPersons({}, printReply)
```

対話モードではなく直接呼び出す場合

```sh
# --evalオプションで実行するコマンドを指定
$ grpcc --proto ./person.proto --address 127.0.0.1:6565 -i --eval 'client.getPersons({}, printReply)'
```

```sh:出力
{
  "person": [
    {
      "name": "Ranga",
      "id": 10001
    },
    {
      "name": "Ravi",
      "id": 10002
    }
  ]
}
```

## 参考

https://github.com/njpatel/grpcc

## ブラウザからgRPCライクにリクエストする

[gRPC Web](https://github.com/improbable-eng/grpc-web)を使う。

## 特徴

- 生成したTypeScriptのコードを使えばクライアント側にも型を強制できる。
- 実際にはhttp/1.1でアクセス。
- クライアントとサーバー間に別途[grpcweb](https://github.com/improbable-eng/grpc-web/tree/master/go/grpcweb)または[gRPC WebProxy](https://github.com/improbable-eng/grpc-web/tree/master/go/grpcwebproxy)が必要。
  - サーバーがgoの場合はgrpcwebを使う
  - それ以外の場合はgRPC WebProxyを使う

## 使い方

protoファイルからコード生成

```sh
$ PROTOC_GEN_TS_PATH="./node_modules/.bin/protoc-gen-ts"

$ OUT_DIR="./src/generated"

$ protoc \
    --plugin="protoc-gen-ts=${PROTOC_GEN_TS_PATH}" \
    --js_out="import_style=commonjs,binary:${OUT_DIR}" \
    --ts_out="service=true:${OUT_DIR}" \
    --proto_path="../protofiles" \
    person.proto
```

生成したコードを使ってTypeScriptで処理を書く

```typescript
import {grpc} from "grpc-web-client";
import {Empty} from "../../generated/person_pb";
import {Person} from "../../generated/person_pb_service";

getPersons() {
    const empty = new Empty()
    grpc.unary(Person.GetPersons, {
        request: empty,
        host: "http://localhost:8080",//gRPC WebProxyを使う場合は、gRPC WebProxyのURLを指定する
        onEnd: res => {
            const {status, statusMessage, headers, message, trailers} = res
            if (status === grpc.Code.OK && message) {
                console.log(message.toObject())
            }
        }
    })
}
```

gRPCサーバーがgoの場合は、grpcwebでラップする。

それ以外の場合は、以下のようにgRPC WebProxyを起動する。 (今回はDockerで起動)

```
$ docker run -p 8080:8080 --rm -it jfyne/docker-grpcwebproxy /grpcwebproxy --backend_addr=host.docker.internal:6565 --run_tls_server=false
```

## 参考

https://github.com/improbable-eng/grpc-web/tree/master/ts
https://github.com/improbable-eng/grpc-web/tree/master/go/grpcwebproxy

# RestAPIライクにリクエストする

[Google Cloud Endpoints](https://cloud.google.com/endpoints/docs/grpc?hl=ja) または [gRPC Gateway](https://github.com/grpc-ecosystem/grpc-gateway)を使う。

## 特徴

- エンドポイントに対応するURLを設定することでRestAPIライクにリクエストできる。
- gRPC Web が使えない事情がある場合、ブラウザからリクエストするにはこちらを使う。

## 使い方

今回検証した Google Cloud Endppoints と Google Kubernetes Engine を連携させるパターンのみ記載する。

Endpoints設定ファイルを作成。ここでエンドポイントのURLを設定する

```proto:api_config.yaml
type: google.api.Service
config_version: 3

name: person.endpoints.[GOOGLE_PROJECT_ID].cloud.goog

title: person gRPC API
apis:
- name: Person

http:
  rules:
  - selector: Person.GetPersons
    get: /v1/persons

usage:
  rules:
  - selector: "*"
    allow_unregistered_calls: true
```

Endpoints設定をデプロイする。

```
$ protoc --include_imports --include_source_info person.proto --descriptor_set_out out.pb
$ gcloud endpoints services deploy out.pb api_config.yaml
```

あとは[チュートリアル](https://cloud.google.com/endpoints/docs/tutorials?hl=ja)の通り進める。

## 参考

- https://cloud.google.com/endpoints/docs/grpc-service-config?hl=ja